Compare commits

...
44 Commits
Author SHA1 Message Date
Julien Duponchelle 8a19afd618 1.4.6 2016-04-28 18:27:33 +02:00
Julien Duponchelle 85f571ee8b Solve wintypes import 2016-04-28 16:35:58 +02:00
grossmj 4fc5364ab5 More robust save/restore for VirtualBox linked clone VM hard disks. 2016-04-26 15:44:11 -06:00
grossmj 4a91d8a6a5 Prevent non linked cloned hard disks to be detached when using VirtualBox linked cloned VMs. Fixes #1184. 2016-04-26 15:06:22 -06:00
grossmj b408f29726 Stricter checks to match VMware version to the right vmrun (VIX library) version.
Also checks the VIX library version when only using the GNS3 VM running in VMware.
2016-04-26 13:12:42 -06:00
Julien Duponchelle 6d6e8196d2 Allow only .pcap to be downloaded from remote stream API
Fix #500
2016-04-26 09:52:18 +02:00
Julien Duponchelle 75196b8a55 Fix incrementation of qemu mac address
Fix #501
2016-04-25 16:36:20 +02:00
Julien Duponchelle 4afbf816ab Fix errors in remote install script 2016-04-14 16:20:13 +02:00
Julien Duponchelle 40aec61701 Ask user to reboot after the remote install script 2016-04-14 16:16:31 +02:00
Julien Duponchelle 7f77c66888 Support VMware VM in non standard location for fusion 2016-04-13 09:38:58 +02:00
Jeremy Grossmann a5b5f6c811 Merge pull request #477 from GNS3/32bitiou
Allow to add 32 bit repositories when they are not available on the system
2016-04-05 10:33:14 -06:00
Julien Duponchelle dc236eba09 Allow to add 32 bit repositories when they are not available on the system 2016-04-05 10:28:32 +02:00
grossmj 6eca621b19 Clear warnings about using linked clones with VMware Player. 2016-04-04 12:10:48 -06:00
grossmj ba006f105f Alternative method to find the Documents folder on Windows. 2016-03-28 19:37:34 -06:00
Jeremy Grossmann e167a81080 Merge pull request #462 from GNS3/remote_improved
Add IOU support and install config in /etc
2016-03-25 12:24:50 -06:00
Julien Duponchelle 9f05b80d01 Add IOU support and install config in /etc 2016-03-25 19:08:14 +01:00
Julien Duponchelle 3fe8016938 1.4.6 dev1 2016-03-23 18:14:04 +01:00
Julien Duponchelle 01dbaea2e4 1.4.5 2016-03-23 18:11:48 +01:00
grossmj 427cb05c2a Stop the VMware VM if there is an error while setting up the network connections or console. 2016-03-23 10:54:45 -06:00
Jeremy Grossmann 39c41336db Merge pull request #459 from GNS3/remote_install
Remote install on 14.04 ubuntu
2016-03-22 15:08:15 -06:00
grossmj fa22e19321 Bump version to 1.4.5dev2 2016-03-22 14:43:47 -06:00
Julien Duponchelle dcc4ddf11b Add a test for \r\n
Fix #458
2016-03-21 16:54:03 +01:00
Julien Duponchelle e35ab1e8c0 Remote install on 14.04 ubuntu 2016-03-21 14:52:42 +01:00
grossmj 975e1c8fa7 Fixes dev syntax bug. 2016-03-20 11:32:40 -06:00
grossmj 19b46a4014 Include VMware VMs paths found preferences.ini 2016-03-19 15:41:26 -06:00
grossmj 7cdf23f497 Allow to stop a VMware VM from GNS3 even if halted within the VM. Fixes #1118. 2016-03-19 12:08:44 -06:00
grossmj 06da40cdcd Keep Dynamips stdout log file in the project directory. 2016-03-19 11:32:15 -06:00
Julien Duponchelle 75212cda3f Improve tests on Travis 2016-03-16 16:34:36 +01:00
grossmj a018c5b0f5 Get MAC addresses for host interfaces to use for filtering frames from vmnet interfaces. 2016-03-14 18:27:51 -06:00
grossmj 93e2d4096d Dynamips uuid hypervisor command is no longer supported. 2016-03-12 22:15:36 -07:00
grossmj c077f3b990 Restart NPF service after adding vmnet adapters on Windows. 2016-03-12 11:04:50 -07:00
Julien Duponchelle 04b02171bb Support /etc/gns3/gns3_server.conf for the config
It's better for a server deployment
2016-02-25 15:07:55 +01:00
Julien Duponchelle e91e3525c4 Improve warning if fusion is not installed or in non standard location
For the moment I don't see a good reason to not installating fusion
in the standard location.  If check in Docker Machine code they don't
support it. And with their large user base we can think it's safe.

Fix #435
2016-02-25 12:15:38 +01:00
Julien Duponchelle ebb8c0d0b0 Increase test timeout to avoid random problems on travis 2016-02-24 15:51:51 +01:00
Julien Duponchelle 4c4613b29b 1.4.5dev1 2016-02-23 15:41:45 +01:00
Julien Duponchelle 9d8cb4521d 1.4.4 2016-02-23 14:38:26 +01:00
Julien Duponchelle 9dd09ccde1 1.4.4dev4 2016-02-22 13:06:56 +01:00
Julien Duponchelle 86ebb9b840 1.4.4dev3 2016-02-22 11:20:10 +01:00
grossmj 569da0ce3a Bump version to 1.4.4dev2 2016-02-20 18:41:38 -07:00
grossmj f73b288a28 Check if VMware Fusion is correctly installed when retrieving the VM list. 2016-02-20 10:59:06 -07:00
grossmj e34c266ee1 1.4.4dev1 2016-02-19 16:02:51 -07:00
grossmj 80e15c86dc 1.4.3 2016-02-19 14:17:54 -07:00
Julien Duponchelle 90109e04aa 1.4.3dev1 2016-02-18 17:46:38 +01:00
Julien Duponchelle c36de3393d 1.4.3dev1 2016-02-17 19:40:23 +01:00
20 changed files with 738 additions and 128 deletions

No files matched your search

+30
View File
@@ -1,5 +1,35 @@
# Change Log
## 1.4.6 28/04/2016
* More robust save/restore for VirtualBox linked clone VM hard disks.
* Prevent non linked cloned hard disks to be detached when using VirtualBox linked cloned VMs. Fixes #1184.
* Stricter checks to match VMware version to the right vmrun (VIX library) version. Also checks the VIX library version when only using the GNS3 VM running in VMware.
* Allow only .pcap to be downloaded from remote stream API
* Fix incrementation of qemu mac address
* Clear warnings about using linked clones with VMware Player.
* Alternative method to find the Documents folder on Windows.
* Add IOU support and install config in /etc
## 1.4.5 23/03/2016
* Stop the VMware VM if there is an error while setting up the network connections or console.
* Remote install on 14.04 ubuntu
* Include VMware VMs paths found preferences.ini
* Allow to stop a VMware VM from GNS3 even if halted within the VM. Fixes #1118.
* Keep Dynamips stdout log file in the project directory.
* Get MAC addresses for host interfaces to use for filtering frames from vmnet interfaces.
* Dynamips uuid hypervisor command is no longer supported.
* Restart NPF service after adding vmnet adapters on Windows.
* Support /etc/gns3/gns3_server.conf for the config
* Improve warning if fusion is not installed or in non standard location
## 1.4.4 23/02/2016
* Check if VMware Fusion is correctly installed when retrieving the VM list.
## 1.4.3 19/02/2016
* Nothing! (changes made in the GUI only).
## 1.4.2 17/02/2016
* Fix missing format in IOU export
* Fix number of arguments to the UDP errors on VBOX
+2 -1
View File
@@ -75,7 +75,7 @@ class Config(object):
# 2: $HOME/.config/GNS3.conf
# 3: /etc/xdg/GNS3/gns3_server.conf
# 4: /etc/xdg/GNS3.conf
# 5: server.conf in the current working directory
# 5: gns3_server.conf in the current working directory
appname = "GNS3"
home = os.path.expanduser("~")
@@ -84,6 +84,7 @@ class Config(object):
self._files = [os.path.join(os.getcwd(), filename),
os.path.join(home, ".config", appname, filename),
os.path.join(home, ".config", appname + ".conf"),
os.path.join("/etc/gns3", filename),
os.path.join("/etc/xdg", appname, filename),
os.path.join("/etc/xdg", appname + ".conf")]
+3
View File
@@ -37,6 +37,9 @@ class FileHandler:
def read(request, response):
response.enable_chunked_encoding()
if not request.json.get("location").endswith(".pcap"):
raise aiohttp.web.HTTPForbidden(text="Only .pcap file are allowed")
try:
with open(request.json.get("location"), "rb") as f:
loop = asyncio.get_event_loop()
@@ -55,7 +55,6 @@ class DynamipsHypervisor:
self._working_dir = working_dir
self._version = "N/A"
self._timeout = timeout
self._uuid = None
self._reader = None
self._writer = None
self._io_lock = asyncio.Lock()
@@ -99,8 +98,6 @@ class DynamipsHypervisor:
except IndexError:
self._version = "Unknown"
self._uuid = yield from self.send("hypervisor uuid")
# this forces to send the working dir to Dynamips
yield from self.set_working_dir(self._working_dir)
@@ -174,16 +171,6 @@ class DynamipsHypervisor:
return self._working_dir
@property
def uuid(self):
"""
Returns this hypervisor UUID.
:Returns: uuid string
"""
return self._uuid
@property
def devices(self):
"""
+3 -5
View File
@@ -21,7 +21,6 @@ Represents a Dynamips hypervisor and starts/stops the associated Dynamips proces
import os
import subprocess
import tempfile
import asyncio
from gns3server.utils.asyncio import wait_for_process_termination
@@ -120,10 +119,9 @@ class Hypervisor(DynamipsHypervisor):
self._command = self._build_command()
try:
log.info("Starting Dynamips: {}".format(self._command))
with tempfile.NamedTemporaryFile(delete=False) as fd:
self._stdout_file = fd.name
log.info("Dynamips process logging to {}".format(fd.name))
self._stdout_file = os.path.join(self.working_dir, "dynamips_i{}_stdout.txt".format(self._id))
log.info("Dynamips process logging to {}".format(self._stdout_file))
with open(self._stdout_file, "w", encoding="utf-8") as fd:
self._process = yield from asyncio.create_subprocess_exec(*self._command,
stdout=fd,
stderr=subprocess.STDOUT,
+3 -1
View File
@@ -40,6 +40,8 @@ from ..base_vm import BaseVM
from ...schemas.qemu import QEMU_OBJECT_SCHEMA, QEMU_PLATFORMS
from ...utils.asyncio import monitor_process
from ...utils.images import md5sum
from ...utils import macaddress_to_int, int_to_macaddress
import logging
log = logging.getLogger(__name__)
@@ -1374,7 +1376,7 @@ class QemuVM(BaseVM):
patched_qemu = True
for adapter_number, adapter in enumerate(self._ethernet_adapters):
mac = "%s%02x" % (self._mac_address[:-2], (int(self._mac_address[-2:]) + adapter_number) % 255)
mac = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number)
nio = adapter.get_nio(0)
if self._legacy_networking:
# legacy QEMU networking syntax (-net)
+85 -43
View File
@@ -159,7 +159,7 @@ class VirtualBoxVM(BaseVM):
if self.id and os.path.isdir(os.path.join(self.working_dir, self._vmname)):
vbox_file = os.path.join(self.working_dir, self._vmname, self._vmname + ".vbox")
yield from self.manager.execute("registervm", [vbox_file])
yield from self._reattach_hdds()
yield from self._reattach_linked_hdds()
else:
yield from self._create_linked_clone()
@@ -314,7 +314,10 @@ class VirtualBoxVM(BaseVM):
return hdds
@asyncio.coroutine
def _reattach_hdds(self):
def _reattach_linked_hdds(self):
"""
Reattach linked cloned hard disks.
"""
hdd_info_file = os.path.join(self.working_dir, self._vmname, "hdd_info.json")
try:
@@ -333,10 +336,67 @@ class VirtualBoxVM(BaseVM):
device=hdd_info["device"],
medium=hdd_file))
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium "{}"'.format(hdd_info["controller"],
hdd_info["port"],
hdd_info["device"],
hdd_file))
try:
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium "{}"'.format(hdd_info["controller"],
hdd_info["port"],
hdd_info["device"],
hdd_file))
except VirtualBoxError as e:
log.warn("VirtualBox VM '{name}' [{id}] error reattaching HDD {controller} {port} {device} {medium}: {error}".format(name=self.name,
id=self.id,
controller=hdd_info["controller"],
port=hdd_info["port"],
device=hdd_info["device"],
medium=hdd_file,
error=e))
continue
@asyncio.coroutine
def save_linked_hdds_info(self):
"""
Save linked cloned hard disks information.
:returns: disk table information
"""
hdd_table = []
if self._linked_clone:
if os.path.exists(self.working_dir):
hdd_files = yield from self._get_all_hdd_files()
vm_info = yield from self._get_vm_info()
for entry, value in vm_info.items():
match = re.search("^([\s\w]+)\-(\d)\-(\d)$", entry) # match Controller-PortNumber-DeviceNumber entry
if match:
controller = match.group(1)
port = match.group(2)
device = match.group(3)
if value in hdd_files and os.path.exists(os.path.join(self.working_dir, self._vmname, "Snapshots", os.path.basename(value))):
log.info("VirtualBox VM '{name}' [{id}] detaching HDD {controller} {port} {device}".format(name=self.name,
id=self.id,
controller=controller,
port=port,
device=device))
hdd_table.append(
{
"hdd": os.path.basename(value),
"controller": controller,
"port": port,
"device": device,
}
)
if hdd_table:
try:
hdd_info_file = os.path.join(self.working_dir, self._vmname, "hdd_info.json")
with open(hdd_info_file, "w", encoding="utf-8") as f:
json.dump(hdd_table, f, indent=4)
except OSError as e:
log.warning("VirtualBox VM '{name}' [{id}] could not write HHD info file: {error}".format(name=self.name,
id=self.id,
error=e.strerror))
return hdd_table
@asyncio.coroutine
def close(self):
@@ -363,47 +423,29 @@ class VirtualBoxVM(BaseVM):
yield from self.stop()
if self._linked_clone:
hdd_table = []
if os.path.exists(self.working_dir):
hdd_files = yield from self._get_all_hdd_files()
vm_info = yield from self._get_vm_info()
for entry, value in vm_info.items():
match = re.search("^([\s\w]+)\-(\d)\-(\d)$", entry)
if match:
controller = match.group(1)
port = match.group(2)
device = match.group(3)
if value in hdd_files:
log.info("VirtualBox VM '{name}' [{id}] detaching HDD {controller} {port} {device}".format(name=self.name,
id=self.id,
controller=controller,
port=port,
device=device))
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium none'.format(controller,
port,
device))
hdd_table.append(
{
"hdd": os.path.basename(value),
"controller": controller,
"port": port,
"device": device,
}
)
hdd_table = yield from self.save_linked_hdds_info()
for hdd in hdd_table.copy():
log.info("VirtualBox VM '{name}' [{id}] detaching HDD {controller} {port} {device}".format(name=self.name,
id=self.id,
controller=hdd["controller"],
port=hdd["port"],
device=hdd["device"]))
try:
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium none'.format(hdd["controller"],
hdd["port"],
hdd["device"]))
except VirtualBoxError as e:
log.warn("VirtualBox VM '{name}' [{id}] error detaching HDD {controller} {port} {device}: {error}".format(name=self.name,
id=self.id,
controller=hdd["controller"],
port=hdd["port"],
device=hdd["device"],
error=e))
continue
log.info("VirtualBox VM '{name}' [{id}] unregistering".format(name=self.name, id=self.id))
yield from self.manager.execute("unregistervm", [self._name])
if hdd_table:
try:
hdd_info_file = os.path.join(self.working_dir, self._vmname, "hdd_info.json")
with open(hdd_info_file, "w", encoding="utf-8") as f:
json.dump(hdd_table, f, indent=4)
except OSError as e:
log.warning("VirtualBox VM '{name}' [{id}] could not write HHD info file: {error}".format(name=self.name,
id=self.id,
error=e.strerror))
log.info("VirtualBox VM '{name}' [{id}] closed".format(name=self.name, id=self.id))
self._closed = True
+105 -29
View File
@@ -35,10 +35,10 @@ from pkg_resources import parse_version
log = logging.getLogger(__name__)
from ..base_manager import BaseManager
from .vmware_vm import VMwareVM
from .vmware_error import VMwareError
from .nio_vmnet import NIOVMNET
from gns3server.modules.base_manager import BaseManager
from gns3server.modules.vmware.vmware_vm import VMwareVM
from gns3server.modules.vmware.vmware_error import VMwareError
from gns3server.modules.vmware.nio_vmnet import NIOVMNET
class VMware(BaseManager):
@@ -108,7 +108,7 @@ class VMware(BaseManager):
vmrun_path = shutil.which("vmrun")
if not vmrun_path:
raise VMwareError("Could not find vmrun")
raise VMwareError("Could not find VMware vmrun, please make sure it is installed")
if not os.path.isfile(vmrun_path):
raise VMwareError("vmrun {} is not accessible".format(vmrun_path))
if not os.access(vmrun_path, os.X_OK):
@@ -137,6 +137,50 @@ class VMware(BaseManager):
version = match.group(1)
return version
@asyncio.coroutine
def _check_vmware_player_requirements(self, player_version):
"""
Check minimum requirements to use VMware Player.
VIX 1.13 was the release for Player 6.
VIX 1.14 was the release for Player 7.
VIX 1.15 was the release for Workstation Player 12.
:param player_version: VMware Player major version.
"""
player_version = int(player_version)
if player_version < 6:
raise VMwareError("Using VMware Player requires version 6 or above")
elif player_version == 6:
yield from self.check_vmrun_version(minimum_required_version="1.13")
elif player_version == 7:
yield from self.check_vmrun_version(minimum_required_version="1.14")
elif player_version >= 12:
yield from self.check_vmrun_version(minimum_required_version="1.15")
@asyncio.coroutine
def _check_vmware_workstation_requirements(self, ws_version):
"""
Check minimum requirements to use VMware Workstation.
VIX 1.13 was the release for Workstation 10.
VIX 1.14 was the release for Workstation 11.
VIX 1.15 was the release for Workstation Pro 12.
:param ws_version: VMware Workstation major version.
"""
ws_version = int(ws_version)
if ws_version < 10:
raise VMwareError("Using VMware Workstation requires version 10 or above")
elif ws_version == 10:
yield from self.check_vmrun_version(minimum_required_version="1.13")
elif ws_version == 11:
yield from self.check_vmrun_version(minimum_required_version="1.14")
elif ws_version >= 12:
yield from self.check_vmrun_version(minimum_required_version="1.15")
@asyncio.coroutine
def check_vmware_version(self):
"""
@@ -150,18 +194,17 @@ class VMware(BaseManager):
player_version = self._find_vmware_version_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Player")
if player_version:
log.debug("VMware Player version {} detected".format(player_version))
if int(player_version) < 6:
raise VMwareError("Using VMware Player requires version 6 or above")
yield from self._check_vmware_player_requirements(player_version)
else:
log.warning("Could not find VMware version")
else:
log.debug("VMware Workstation version {} detected".format(ws_version))
if int(ws_version) < 10:
raise VMwareError("Using VMware Workstation requires version 10 or above")
return
yield from self._check_vmware_workstation_requirements(ws_version)
else:
if sys.platform.startswith("darwin"):
return # FIXME: no version checking on Mac OS X
if not os.path.isdir("/Applications/VMware Fusion.app"):
raise VMwareError("VMware Fusion is not installed in the standard location /Applications/VMware Fusion.app")
return # FIXME: no version checking on Mac OS X but we support all versions of fusion
vmware_path = VMware._get_linux_vmware_binary()
if vmware_path is None:
@@ -172,16 +215,16 @@ class VMware(BaseManager):
match = re.search("VMware Workstation ([0-9]+)\.", output)
version = None
if match:
# VMware Workstation has been detected
version = match.group(1)
log.debug("VMware Workstation version {} detected".format(version))
if int(version) < 10:
raise VMwareError("Using VMware Workstation requires version 10 or above")
yield from self._check_vmware_workstation_requirements(version)
match = re.search("VMware Player ([0-9]+)\.", output)
if match:
# VMware Player has been detected
version = match.group(1)
log.debug("VMware Player version {} detected".format(version))
if int(version) < 6:
raise VMwareError("Using VMware Player requires version 6 or above")
yield from self._check_vmware_player_requirements(version)
if version is None:
log.warning("Could not find VMware version. Output of VMware: {}".format(output))
raise VMwareError("Could not find VMware version. Output of VMware: {}".format(output))
@@ -350,7 +393,17 @@ class VMware(BaseManager):
return stdout_data.decode("utf-8", errors="ignore").splitlines()
@asyncio.coroutine
def check_vmrun_version(self):
def check_vmrun_version(self, minimum_required_version="1.13"):
"""
Checks the vmrun version.
VMware VIX library version must be at least >= 1.13 by default
VIX 1.13 was the release for VMware Fusion 6, Workstation 10, and Player 6.
VIX 1.14 was the release for VMware Fusion 7, Workstation 11 and Player 7.
VIX 1.15 was the release for VMware Fusion 8, Workstation Pro 12 and Workstation Player 12.
:param required_version: required vmrun version number
"""
with (yield from self._execute_lock):
vmrun_path = self.vmrun_path
@@ -364,9 +417,9 @@ class VMware(BaseManager):
if match:
version = match.group(1)
log.debug("VMware vmrun version {} detected".format(version))
if parse_version(version) < parse_version("1.13"):
# VMware VIX library version must be at least >= 1.13
raise VMwareError("VMware vmrun executable version must be >= version 1.13")
if parse_version(version) < parse_version(minimum_required_version):
raise VMwareError("VMware vmrun executable version must be >= version {}".format(minimum_required_version))
if version is None:
log.warning("Could not find VMware vmrun version. Output: {}".format(output))
raise VMwareError("Could not find VMware vmrun version. Output: {}".format(output))
@@ -592,8 +645,11 @@ class VMware(BaseManager):
"""
if sys.platform.startswith("win"):
from win32com.shell import shell, shellcon
documents_folder = shell.SHGetSpecialFolderPath(None, shellcon.CSIDL_PERSONAL)
import ctypes
import ctypes.wintypes
path = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
ctypes.windll.shell32.SHGetFolderPathW(None, 5, None, 0, path)
documents_folder = path.value
windows_type = sys.getwindowsversion().product_type
if windows_type == 2 or windows_type == 3:
return '{}\My Virtual Machines'.format(documents_folder)
@@ -614,26 +670,39 @@ class VMware(BaseManager):
yield from self.check_vmware_version()
inventory_path = self.get_vmware_inventory_path()
if os.path.exists(inventory_path):
# FIXME: inventory may exist if VMware workstation has not been fully uninstalled, therefore VMware player VMs are not searched
if os.path.exists(inventory_path) and self.host_type != "player":
# inventory may exist for VMware player if VMware workstation has been previously installed
return self._get_vms_from_inventory(inventory_path)
else:
# VMware player has no inventory file, let's search the default location for VMs.
# VMware player has no inventory file, let's search the default location for VMs
vmware_preferences_path = self.get_vmware_preferences_path()
default_vm_path = self.get_vmware_default_vm_path()
pairs = {}
if os.path.exists(vmware_preferences_path):
# the default vm path may be present in VMware preferences file.
try:
pairs = self.parse_vmware_file(vmware_preferences_path)
if "prefvmx.defaultvmpath" in pairs:
default_vm_path = pairs["prefvmx.defaultvmpath"]
except OSError as e:
log.warning('Could not read VMware preferences file "{}": {}'.format(vmware_preferences_path, e))
if "prefvmx.defaultvmpath" in pairs:
default_vm_path = pairs["prefvmx.defaultvmpath"]
if not os.path.isdir(default_vm_path):
raise VMwareError('Could not find the default VM directory: "{}"'.format(default_vm_path))
return self._get_vms_from_directory(default_vm_path)
vms = self._get_vms_from_directory(default_vm_path)
# looks for VMX paths in the preferences file in case not all VMs are in the default directory
for key, value in pairs.items():
m = re.match(r'pref.mruVM(\d+)\.filename', key)
if m:
display_name = "pref.mruVM{}.displayName".format(m.group(1))
if display_name in pairs:
found = False
for vm in vms:
if vm["vmname"] == display_name:
found = True
if found is False:
vms.append({"vmname": pairs[display_name], "vmx_path": value})
return vms
@staticmethod
def _get_linux_vmware_binary():
@@ -644,3 +713,10 @@ class VMware(BaseManager):
if path is None:
path = shutil.which("vmplayer")
return path
if __name__ == '__main__':
loop = asyncio.get_event_loop()
vmware = VMware.instance()
print("=> Check version")
loop.run_until_complete(asyncio.async(vmware.check_vmware_version()))
+45 -21
View File
@@ -26,7 +26,7 @@ import asyncio
import tempfile
from gns3server.utils.telnet_server import TelnetServer
from gns3server.utils.interfaces import get_windows_interfaces
from gns3server.utils.interfaces import interfaces, get_windows_interfaces
from gns3server.utils.asyncio import wait_for_file_creation, wait_for_named_pipe_creation
from collections import OrderedDict
from .vmware_error import VMwareError
@@ -144,6 +144,8 @@ class VMwareVM(BaseVM):
yield from self.manager.check_vmrun_version()
if self._linked_clone and not os.path.exists(os.path.join(self.working_dir, os.path.basename(self._vmx_path))):
if self.manager.host_type == "player":
raise VMwareError("Linked clones are not supported by VMware Player")
# create the base snapshot for linked clones
base_snapshot_name = "GNS3 Linked Base for clones"
vmsd_path = os.path.splitext(self._vmx_path)[0] + ".vmsd"
@@ -318,20 +320,29 @@ class VMwareVM(BaseVM):
vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
if sys.platform.startswith("linux"):
yield from self._ubridge_hypervisor.send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=vnet,
interface=vmnet_interface))
interface=vmnet_interface))
elif sys.platform.startswith("win"):
windows_interfaces = get_windows_interfaces()
npf = None
source_mac = None
for interface in windows_interfaces:
if "netcard" in interface and vmnet_interface in interface["netcard"]:
npf = interface["id"]
source_mac = interface["mac_address"]
elif vmnet_interface in interface["name"]:
npf = interface["id"]
source_mac = interface["mac_address"]
if npf:
yield from self._ubridge_hypervisor.send('bridge add_nio_ethernet {name} "{interface}"'.format(name=vnet,
interface=npf))
else:
raise VMwareError("Could not find NPF id for VMnet interface {}".format(vmnet_interface))
# TODO: should provide that as an option
#if source_mac:
# yield from self._ubridge_hypervisor.send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=vnet,
# mac=source_mac))
elif sys.platform.startswith("darwin"):
yield from self._ubridge_hypervisor.send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet,
interface=vmnet_interface))
@@ -351,6 +362,14 @@ class VMwareVM(BaseVM):
yield from self._ubridge_hypervisor.send('bridge start {name}'.format(name=vnet))
# TODO: this only work when using PCAP (NIO Ethernet)
# source_mac = None
# for interface in interfaces():
# if interface["name"] == vmnet_interface:
# source_mac = interface["mac_address"]
# if source_mac:
# yield from self._ubridge_hypervisor.send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=vnet, mac=source_mac))
@asyncio.coroutine
def _delete_ubridge_connection(self, adapter_number):
"""
@@ -437,21 +456,25 @@ class VMwareVM(BaseVM):
else:
yield from self._control_vm("start")
if self._use_ubridge and self._ubridge_hypervisor:
for adapter_number in range(0, self._adapters):
nio = self._ethernet_adapters[adapter_number].get_nio(0)
if nio:
yield from self._add_ubridge_connection(nio, adapter_number)
try:
if self._use_ubridge and self._ubridge_hypervisor:
for adapter_number in range(0, self._adapters):
nio = self._ethernet_adapters[adapter_number].get_nio(0)
if nio:
yield from self._add_ubridge_connection(nio, adapter_number)
if self._enable_remote_console and self._console is not None:
try:
if sys.platform.startswith("win"):
yield from wait_for_named_pipe_creation(self._get_pipe_name())
else:
yield from wait_for_file_creation(self._get_pipe_name()) # wait for VMware to create the pipe file.
except asyncio.TimeoutError:
raise VMwareError('Pipe file "{}" for remote console has not been created by VMware'.format(self._get_pipe_name()))
self._start_remote_console()
if self._enable_remote_console and self._console is not None:
try:
if sys.platform.startswith("win"):
yield from wait_for_named_pipe_creation(self._get_pipe_name())
else:
yield from wait_for_file_creation(self._get_pipe_name()) # wait for VMware to create the pipe file.
except asyncio.TimeoutError:
raise VMwareError('Pipe file "{}" for remote console has not been created by VMware'.format(self._get_pipe_name()))
self._start_remote_console()
except VMwareError:
yield from self.stop()
raise
if self._get_vmx_setting("vhv.enable", "TRUE"):
self._hw_virtualization = True
@@ -471,11 +494,12 @@ class VMwareVM(BaseVM):
yield from self._ubridge_hypervisor.stop()
try:
if self.acpi_shutdown:
# use ACPI to shutdown the VM
yield from self._control_vm("stop", "soft")
else:
yield from self._control_vm("stop")
if (yield from self.is_running()):
if self.acpi_shutdown:
# use ACPI to shutdown the VM
yield from self._control_vm("stop", "soft")
else:
yield from self._control_vm("stop")
finally:
self._started = False
+18
View File
@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import textwrap
import posixpath
@@ -26,3 +27,20 @@ def force_unix_path(path):
path = path.replace("\\", "/")
return posixpath.normpath(path)
def macaddress_to_int(macaddress):
"""
Convert a macaddress with the format 00:0c:29:11:b0:0a to a int
:param macaddress: The mac address
:returns: Integer
"""
return int(macaddress.replace(":", ""), 16)
def int_to_macaddress(integer):
"""
Convert an integer to a macaddress
"""
return ":".join(textwrap.wrap("%012x" % (integer), width=2))
+8 -3
View File
@@ -23,7 +23,7 @@ import struct
import psutil
if psutil.version_info < (3, 0, 0):
raise Exception("psutil version should >= 3.0.0. If you are under ubuntu/debian install gns3 via apt instead of pip")
raise Exception("psutil version should >= 3.0.0. If you are under Ubuntu/Debian install gns3 via apt instead of pip")
import logging
log = logging.getLogger(__name__)
@@ -59,6 +59,7 @@ def _get_windows_interfaces_from_registry():
interfaces.append({"id": npf_interface,
"name": name,
"ip_address": ip_address,
"mac_address": "", # TODO: find MAC address in registry
"netcard": netcard})
winreg.CloseKey(hkeyinterface)
winreg.CloseKey(hkeycon)
@@ -99,6 +100,7 @@ def get_windows_interfaces():
interfaces.append({"id": npf_interface,
"name": adapter.NetConnectionID,
"ip_address": ip_address,
"mac_address": adapter.MACAddress,
"netcard": adapter.name})
except (AttributeError, pywintypes.com_error):
log.warn("Could not use the COM service to retrieve interface info, trying using the registry...")
@@ -148,14 +150,17 @@ def interfaces():
if not sys.platform.startswith("win"):
for interface in sorted(psutil.net_if_addrs().keys()):
ip_address = ""
mac_address = ""
for addr in psutil.net_if_addrs()[interface]:
# get the first available IPv4 address only
if addr.family == socket.AF_INET:
ip_address = addr.address
break
if addr.family == psutil.AF_LINK:
mac_address = addr.address
results.append({"id": interface,
"name": interface,
"ip_address": ip_address})
"ip_address": ip_address,
"mac_address": mac_address})
else:
try:
results = get_windows_interfaces()
+2 -2
View File
@@ -23,5 +23,5 @@
# or negative for a release candidate or beta (after the base version
# number has been incremented)
__version__ = "1.4.2"
__version_info__ = (1, 4, 2, 0)
__version__ = "1.4.6"
__version_info__ = (1, 4, 6, 0)
+338
View File
@@ -0,0 +1,338 @@
#!/bin/bash
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Install GNS3 on a remote Ubuntu 14.04 LTS server
# This create a dedicated user and setup all the package
# and optionnaly a VPN
#
function help {
echo "Usage:" >&2
echo "--with-openvpn: Install Open VPN" >&2
echo "--with-iou: Install IOU" >&2
echo "--with-i386-repository: Add i386 repositories require by IOU if they are not available on the system. Warning this will replace your source.list in order to use official ubuntu mirror" >&2
echo "--help: This help" >&2
}
function log {
tput setaf 2
echo "=> $1" >&2
tput sgr0
}
lsb_release -d | grep "Ubuntu 14.04" > /dev/null
if [ $? != 0 ]
then
echo "You can use this script on Ubuntu 14.04 LTS only"
exit 1
fi
# Read the options
USE_VPN=0
USE_IOU=0
I386_REPO=0
TEMP=`getopt -o h --long with-openvpn,with-iou,with-i386-repository,help -n 'gns3-remote-install.sh' -- "$@"`
if [ $? != 0 ]
then
help
exit 1
fi
eval set -- "$TEMP"
# extract options and their arguments into variables.
while true ; do
case "$1" in
--with-openvpn)
USE_VPN=1
shift
;;
--with-iou)
USE_IOU=1
shift
;;
--with-i386-repository)
I386_REPO=1
shift
;;
-h|--help)
help
exit 1
;;
--) shift ; break ;;
*) echo "Internal error! $1" ; exit 1 ;;
esac
done
# Exit in case of error
set -e
export DEBIAN_FRONTEND="noninteractive"
log "Add GNS3 repository"
cat <<EOFLIST > /etc/apt/sources.list.d/gns3.list
deb http://ppa.launchpad.net/gns3/ppa/ubuntu trusty main
deb-src http://ppa.launchpad.net/gns3/ppa/ubuntu trusty main
deb http://ppa.launchpad.net/gns3/qemu/ubuntu trusty main
deb-src http://ppa.launchpad.net/gns3/qemu/ubuntu trusty main
EOFLIST
if [ $I386_REPO == 1 ]
then
cat <<EOFLIST2 >> /etc/apt/sources.list
###### Ubuntu Main Repos
deb http://archive.ubuntu.com/ubuntu/ trusty main universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ trusty main universe multiverse
###### Ubuntu Update Repos
deb http://archive.ubuntu.com/ubuntu/ trusty-security main universe multiverse
deb http://archive.ubuntu.com/ubuntu/ trusty-updates main universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ trusty-security main universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ trusty-updates main universe multiverse
EOFLIST2
fi
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A2E3EF7B
log "Update system packages"
apt-get update
log "Upgrade packages"
apt-get upgrade -y
log " Install GNS3 packages"
apt-get install -y gns3-server
log "Create user GNS3 with /opt/gns3 as home directory"
if [ ! -d "/opt/gns3/" ]
then
useradd -d /opt/gns3/ -m gns3
fi
log "Install docker"
if [ ! -f "/usr/bin/docker" ]
then
curl -sSL https://get.docker.com | bash
fi
log "Add GNS3 to the docker group"
usermod -aG docker gns3
if [ $USE_IOU == 1 ]
then
log "IOU setup"
dpkg --add-architecture i386
apt-get update
apt-get install -y gns3-iou
# Force the host name to gns3vm
hostnamectl set-hostname gns3vm
# Force hostid for IOU
dd if=/dev/zero bs=4 count=1 of=/etc/hostid
# Block iou call. The server is down
echo "127.0.0.254 xml.cisco.com" | tee --append /etc/hosts
fi
log "Add gns3 to the kvm group"
usermod -aG kvm gns3
log "Setup GNS3 server"
mkdir -p /etc/gns3
cat <<EOFC > /etc/gns3/gns3_server.conf
[Server]
host = 0.0.0.0
port = 8000
images_path = /opt/gns3/images
projects_path = /opt/gns3/projects
report_errors = True
[Qemu]
enable_kvm = True
EOFC
chown -R gns3:gns3 /etc/gns3
chmod -R 700 /etc/gns3
cat <<EOFI > /etc/init/gns3.conf
description "GNS3 server"
author "GNS3 Team"
start on filesystem or runlevel [2345]
stop on runlevel [016]
respawn
console log
script
exec start-stop-daemon --start --make-pidfile --pidfile /var/run/gns3.pid --chuid gns3 --exec "/usr/bin/gns3server"
end script
pre-start script
echo "" > /var/log/upstart/gns3.log
echo "[`date`] GNS3 Starting"
end script
pre-stop script
echo "[`date`] GNS3 Stopping"
end script
EOFI
chown root:root /etc/init/gns3.conf
chmod 644 /etc/init/gns3.conf
log "Start GNS3 service"
set +e
service gns3 stop
set -e
service gns3 start
log "GNS3 installed with success"
if [ $USE_VPN == 1 ]
then
log "Setup VPN"
cat <<EOFSERVER > /etc/gns3/gns3_server.conf
[Server]
host = 172.16.253.1
port = 8000
images_path = /opt/gns3/images
projects_path = /opt/gns3/projects
report_errors = True
[Qemu]
enable_kvm = True
EOFSERVER
log "Install packages for Open VPN"
apt-get install -y \
openvpn \
uuid \
dnsutils \
nginx-light
MY_IP_ADDR=$(dig @ns1.google.com -t txt o-o.myaddr.l.google.com +short | sed 's/"//g')
log "IP detected: $MY_IP_ADDR"
UUID=$(uuid)
log "Update motd"
cat <<EOFMOTD > /etc/update-motd.d/70-openvpn
#!/bin/sh
echo ""
echo "_______________________________________________________________________________________________"
echo "Download the VPN configuration here:"
echo "http://$MY_IP_ADDR:8003/$UUID/$HOSTNAME.ovpn"
echo ""
echo "And add it to your openvpn client."
echo ""
echo "apt-get remove nginx-light to disable the HTTP server."
echo "And remove this file with rm /etc/update-motd.d/70-openvpn"
EOFMOTD
chmod 755 /etc/update-motd.d/70-openvpn
mkdir -p /etc/openvpn/
[ -d /dev/net ] || mkdir -p /dev/net
[ -c /dev/net/tun ] || mknod /dev/net/tun c 10 200
log "Create keys"
[ -f /etc/openvpn/dh.pem ] || openssl dhparam -out /etc/openvpn/dh.pem 2048
[ -f /etc/openvpn/key.pem ] || openssl genrsa -out /etc/openvpn/key.pem 2048
chmod 600 /etc/openvpn/key.pem
[ -f /etc/openvpn/csr.pem ] || openssl req -new -key /etc/openvpn/key.pem -out /etc/openvpn/csr.pem -subj /CN=OpenVPN/
[ -f /etc/openvpn/cert.pem ] || openssl x509 -req -in /etc/openvpn/csr.pem -out /etc/openvpn/cert.pem -signkey /etc/openvpn/key.pem -days 24855
log "Create client configuration"
cat <<EOFCLIENT > /root/client.ovpn
client
nobind
comp-lzo
dev tun
<key>
`cat /etc/openvpn/key.pem`
</key>
<cert>
`cat /etc/openvpn/cert.pem`
</cert>
<ca>
`cat /etc/openvpn/cert.pem`
</ca>
<dh>
`cat /etc/openvpn/dh.pem`
</dh>
<connection>
remote $MY_IP_ADDR 1194 udp
</connection>
EOFCLIENT
cat <<EOFUDP > /etc/openvpn/udp1194.conf
server 172.16.253.0 255.255.255.0
verb 3
duplicate-cn
comp-lzo
key key.pem
ca cert.pem
cert cert.pem
dh dh.pem
keepalive 10 60
persist-key
persist-tun
proto udp
port 1194
dev tun1194
status openvpn-status-1194.log
log-append /var/log/openvpn-udp1194.log
EOFUDP
echo "Setup HTTP server for serving client certificate"
mkdir -p /usr/share/nginx/openvpn/$UUID
cp /root/client.ovpn /usr/share/nginx/openvpn/$UUID/$HOSTNAME.ovpn
touch /usr/share/nginx/openvpn/$UUID/index.html
touch /usr/share/nginx/openvpn/index.html
cat <<EOFNGINX > /etc/nginx/sites-available/openvpn
server {
listen 8003;
root /usr/share/nginx/openvpn;
}
EOFNGINX
[ -f /etc/nginx/sites-enabled/openvpn ] || ln -s /etc/nginx/sites-available/openvpn /etc/nginx/sites-enabled/
service nginx stop
service nginx start
log "Restart OpenVPN"
set +e
service openvpn stop
service openvpn start
log "Download http://$MY_IP_ADDR:8003/$UUID/$HOSTNAME.ovpn to setup your OpenVPN client after rebooting the server"
fi
+13 -4
View File
@@ -81,16 +81,25 @@ def _get_unused_port():
def server(request, loop, port_manager, monkeypatch):
"""A GNS3 server"""
port = _get_unused_port()
host = "localhost"
app = web.Application()
for method, route, handler in Route.get_routes():
app.router.add_route(method, route, handler)
for module in MODULES:
instance = module.instance()
instance.port_manager = port_manager
srv = loop.create_server(app.make_handler(), host, port)
srv = loop.run_until_complete(srv)
host = "localhost"
# We try multiple time. Because on Travis test can fail when because the port is taken by someone else
for i in range(0, 5):
port = _get_unused_port()
try:
srv = loop.create_server(app.make_handler(), host, port)
srv = loop.run_until_complete(srv)
except OSError:
pass
else:
break
def tear_down():
for module in MODULES:
+19 -4
View File
@@ -27,15 +27,15 @@ from gns3server.version import __version__
def test_stream(server, tmpdir, loop):
with open(str(tmpdir / "test"), 'w+') as f:
with open(str(tmpdir / "test.pcap"), 'w+') as f:
f.write("hello")
def go(future):
query = json.dumps({"location": str(tmpdir / "test")})
query = json.dumps({"location": str(tmpdir / "test.pcap")})
headers = {'content-type': 'application/json'}
response = yield from aiohttp.request("GET", server.get_url("/files/stream", 1), data=query, headers=headers)
response.body = yield from response.content.read(5)
with open(str(tmpdir / "test"), 'a') as f:
with open(str(tmpdir / "test.pcap"), 'a') as f:
f.write("world")
response.body += yield from response.content.read(5)
response.close()
@@ -48,7 +48,8 @@ def test_stream(server, tmpdir, loop):
assert response.body == b'helloworld'
def test_stream_file_not_found(server, tmpdir, loop):
def test_stream_file_not_pcap(server, tmpdir, loop):
def go(future):
query = json.dumps({"location": str(tmpdir / "test")})
headers = {'content-type': 'application/json'}
@@ -56,6 +57,20 @@ def test_stream_file_not_found(server, tmpdir, loop):
response.close()
future.set_result(response)
future = asyncio.Future()
asyncio.async(go(future))
response = loop.run_until_complete(future)
assert response.status == 403
def test_stream_file_not_found(server, tmpdir, loop):
def go(future):
query = json.dumps({"location": str(tmpdir / "test.pcap")})
headers = {'content-type': 'application/json'}
response = yield from aiohttp.request("GET", server.get_url("/files/stream", 1), data=query, headers=headers)
response.close()
future.set_result(response)
future = asyncio.Future()
asyncio.async(go(future))
response = loop.run_until_complete(future)
+9
View File
@@ -118,6 +118,7 @@ def test_start(loop, vm, monkeypatch):
assert vm.is_running()
assert vm.command_line == ' '.join(mock_exec.call_args[0])
def test_start_with_iourc(loop, vm, monkeypatch, tmpdir):
fake_file = str(tmpdir / "iourc")
@@ -412,6 +413,14 @@ def test_iourc_content(vm):
assert f.read() == "test"
def test_iourc_content_fix_carriage_return(vm):
vm.iourc_content = "test\r\n12"
with open(os.path.join(vm.temporary_directory, "iourc")) as f:
assert f.read() == "test\n12"
def test_extract_configs(vm):
assert vm.extract_configs() == (None, None)
+43
View File
@@ -419,6 +419,49 @@ def test_build_command_without_display(vm, loop, fake_qemu_binary):
assert "-nographic" in cmd
def test_build_command_two_adapters(vm, loop, fake_qemu_binary, port_manager):
vm.adapters = 2
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
assert cmd == [
fake_qemu_binary,
"-name",
"test",
"-m",
"256M",
"-smp",
"cpus=1",
"-boot",
"order=c",
"-serial",
"telnet:127.0.0.1:{},server,nowait".format(vm.console),
"-net",
"none",
"-device",
"e1000,mac=00:00:ab:0e:0f:00",
"-device",
"e1000,mac=00:00:ab:0e:0f:01",
"-nographic"
]
def test_build_command_two_adapters_mac_address(vm, loop, fake_qemu_binary, port_manager):
vm.adapters = 2
vm.mac_address = "00:00:ab:0e:0f:09"
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
assert "e1000,mac=00:00:ab:0e:0f:09" in cmd
assert "e1000,mac=00:00:ab:0e:0f:0a" in cmd
vm.mac_address = "00:00:ab:0e:0f:0a"
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process:
cmd = loop.run_until_complete(asyncio.async(vm._build_command()))
assert "e1000,mac=00:00:ab:0e:0f:0a" in cmd
assert "e1000,mac=00:00:ab:0e:0f:0b" in cmd
# Windows accept this kind of mistake
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
def test_build_command_with_invalid_options(vm, loop, fake_qemu_binary):
+9 -1
View File
@@ -16,10 +16,18 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from gns3server.utils import force_unix_path
from gns3server.utils import force_unix_path, macaddress_to_int, int_to_macaddress
def test_force_unix_path():
assert force_unix_path("a/b") == "a/b"
assert force_unix_path("a\\b") == "a/b"
assert force_unix_path("a\\b\\..\\c") == "a/c"
def test_macaddress_to_int():
assert macaddress_to_int("00:0c:29:11:b0:0a") == 52228632586
def test_int_to_macaddress():
assert int_to_macaddress(52228632586) == "00:0c:29:11:b0:0a"
+1 -1
View File
@@ -14,4 +14,4 @@ exclude = tests/*
[pytest]
norecursedirs = .tox
timeout = 5
timeout = 15
+2
View File
@@ -166,6 +166,8 @@ def vmnet_windows(args, vmnet_range_start, vmnet_range_end):
continue
print("Adding vmnet{}...".format(vmnet_number))
os.system('"{}" -- add adapter vmnet{}'.format(vnetlib_path, vmnet_number))
os.system("net stop npf")
os.system("net start npf")
def vmnet_unix(args, vmnet_range_start, vmnet_range_end):