Merge branch 'master' into unstable

Conflicts:
	tests/modules/test_manager.py
This commit is contained in:
Julien Duponchelle committed 2015-04-28 10:46:59 +02:00
commit 5f6d1bfdd6
43 files changed
+608 -677

No files matched your search

+5 -3
View File
@@ -25,6 +25,8 @@ from .qemu import Qemu
MODULES = [VPCS, VirtualBox, Dynamips, Qemu]
if sys.platform.startswith("linux") or hasattr(sys, "_called_from_test") or os.environ.get("PYTEST_BUILD_DOCUMENTATION") == "1":
# IOU runs only on Linux
from .iou import IOU
MODULES.append(IOU)
# IOU runs only on Linux but testsuite work on UNIX platform
if not sys.platform.startswith("win"):
from .iou import IOU
MODULES.append(IOU)
+8 -3
View File
@@ -364,8 +364,9 @@ class BaseManager:
nio = NIOUDP(lport, rhost, rport)
elif nio_settings["type"] == "nio_tap":
tap_device = nio_settings["tap_device"]
if not self._has_privileged_access(executable):
raise aiohttp.web.HTTPForbidden(text="{} has no privileged access to {}.".format(executable, tap_device))
# FIXME: check for permissions on tap device
# if not self._has_privileged_access(executable):
# raise aiohttp.web.HTTPForbidden(text="{} has no privileged access to {}.".format(executable, tap_device))
nio = NIOTAP(tap_device)
elif nio_settings["type"] == "nio_generic_ethernet":
nio = NIOGenericEthernet(nio_settings["ethernet_device"])
@@ -380,8 +381,9 @@ class BaseManager:
:return: file path
"""
if not path:
return ""
img_directory = self.get_images_directory()
if not os.path.isabs(path):
s = os.path.split(path)
return os.path.normpath(os.path.join(img_directory, *s))
@@ -397,6 +399,8 @@ class BaseManager:
:return: file path
"""
if not path:
return ""
img_directory = self.get_images_directory()
path = self.get_abs_image_path(path)
if os.path.dirname(path) == img_directory:
@@ -426,4 +430,5 @@ class BaseManager:
"""
Get the image directory on disk
"""
raise NotImplementedError
+1 -1
View File
@@ -208,7 +208,7 @@ class Dynamips(BaseManager):
"""
# save the configs when the project is committed
for vm in self._vms.values():
for vm in self._vms.copy().values():
if vm.project.id == project.id:
yield from vm.save_configs()
@@ -284,7 +284,7 @@ class DynamipsHypervisor:
if not line:
raise DynamipsError("No data returned from {host}:{port}, Dynamips process running: {run}"
.format(host=self._host, port=self._port, run=self.is_running()))
buf += line.decode()
buf += line.decode("utf-8")
except OSError as e:
raise DynamipsError("Lost communication with {host}:{port} :{error}, Dynamips process running: {run}"
.format(host=self._host, port=self._port, error=e, run=self.is_running()))
+2 -2
View File
@@ -158,8 +158,8 @@ class Hypervisor(DynamipsHypervisor):
output = ""
if self._stdout_file and os.access(self._stdout_file, os.R_OK):
try:
with open(self._stdout_file, errors="replace") as file:
output = file.read()
with open(self._stdout_file, "rb") as file:
output = file.read().decode("utf-8", errors="replace")
except OSError as e:
log.warn("could not read {}: {}".format(self._stdout_file, e))
return output
+1 -1
View File
@@ -120,7 +120,7 @@ class C7200(Router):
npe-225, npe-300, npe-400 and npe-g2 (PowerPC c7200 only)
"""
if self.is_running():
if (yield from self.is_running()):
raise DynamipsError("Cannot change NPE on running router")
yield from self._hypervisor.send('c7200 set_npe "{name}" {npe}'.format(name=self._name, npe=npe))
+4 -4
View File
@@ -1396,7 +1396,7 @@ class Router(BaseVM):
startup_config_path = os.path.join(module_workdir, "configs", "i{}_startup-config.cfg".format(self._dynamips_id))
if os.path.isfile(startup_config_path):
try:
with open(startup_config_path, "r+", errors="replace") as f:
with open(startup_config_path, "r+", encoding="utf-8", errors="replace") as f:
old_config = f.read()
new_config = old_config.replace(self.name, new_name)
f.seek(0)
@@ -1409,7 +1409,7 @@ class Router(BaseVM):
private_config_path = os.path.join(module_workdir, "configs", "i{}_private-config.cfg".format(self._dynamips_id))
if os.path.isfile(private_config_path):
try:
with open(private_config_path, "r+", errors="replace") as f:
with open(private_config_path, "r+", encoding="utf-8", errors="replace") as f:
old_config = f.read()
new_config = old_config.replace(self.name, new_name)
f.seek(0)
@@ -1484,7 +1484,7 @@ class Router(BaseVM):
startup_config_base64, private_config_base64 = yield from self.extract_config()
if startup_config_base64:
try:
config = base64.b64decode(startup_config_base64).decode(errors='replace')
config = base64.b64decode(startup_config_base64).decode("utf-8", errors="replace")
config = "!\n" + config.replace("\r", "")
config_path = os.path.join(module_workdir, self.startup_config)
with open(config_path, "wb") as f:
@@ -1495,7 +1495,7 @@ class Router(BaseVM):
if private_config_base64:
try:
config = base64.b64decode(private_config_base64).decode(errors='replace')
config = base64.b64decode(private_config_base64).decode("utf-8", errors="replace")
config = "!\n" + config.replace("\r", "")
config_path = os.path.join(module_workdir, self.private_config)
with open(config_path, "wb") as f:
+25 -20
View File
@@ -332,8 +332,8 @@ class IOUVM(BaseVM):
def iourc_content(self):
try:
with open(os.path.join(self.temporary_directory, "iourc")) as f:
return f.read()
with open(os.path.join(self.temporary_directory, "iourc"), "rb") as f:
return f.read().decode("utf-8")
except OSError:
return None
@@ -343,8 +343,8 @@ class IOUVM(BaseVM):
if value is not None:
path = os.path.join(self.temporary_directory, "iourc")
try:
with open(path, "w+") as f:
f.write(value)
with open(path, "wb+") as f:
f.write(value.encode("utf-8"))
except OSError as e:
raise IOUError("Could not write the iourc file {}: {}".format(path, e))
@@ -378,12 +378,14 @@ class IOUVM(BaseVM):
config = configparser.ConfigParser()
try:
with open(self.iourc_path) as f:
with open(self.iourc_path, encoding="utf-8") as f:
config.read_file(f)
except OSError as e:
raise IOUError("Could not open iourc file {}: {}".format(self.iourc_path, e))
except configparser.Error as e:
raise IOUError("Could not parse iourc file {}: {}".format(self.iourc_path, e))
except UnicodeDecodeError as e:
raise IOUError("Invalid iourc file {}: {}".format(self.iourc_path, e))
if "license" not in config:
raise IOUError("License section not found in iourc file {}".format(self.iourc_path))
hostname = socket.gethostname()
@@ -455,7 +457,7 @@ class IOUVM(BaseVM):
log.info("Starting IOU: {}".format(self._command))
self._iou_stdout_file = os.path.join(self.working_dir, "iou.log")
log.info("Logging to {}".format(self._iou_stdout_file))
with open(self._iou_stdout_file, "w") as fd:
with open(self._iou_stdout_file, "w", encoding="utf-8") as fd:
self._iou_process = yield from asyncio.create_subprocess_exec(*self._command,
stdout=fd,
stderr=subprocess.STDOUT,
@@ -499,7 +501,7 @@ class IOUVM(BaseVM):
log.info("starting iouyap: {}".format(command))
self._iouyap_stdout_file = os.path.join(self.working_dir, "iouyap.log")
log.info("logging to {}".format(self._iouyap_stdout_file))
with open(self._iouyap_stdout_file, "w") as fd:
with open(self._iouyap_stdout_file, "w", encoding="utf-8") as fd:
self._iouyap_process = yield from asyncio.create_subprocess_exec(*command,
stdout=fd,
stderr=subprocess.STDOUT,
@@ -565,7 +567,7 @@ class IOUVM(BaseVM):
bay_id += 1
try:
with open(iouyap_ini, "w") as config_file:
with open(iouyap_ini, "w", encoding="utf-8") as config_file:
config.write(config_file)
log.info("IOU {name} [id={id}]: iouyap.ini updated".format(name=self._name,
id=self._id))
@@ -587,7 +589,6 @@ class IOUVM(BaseVM):
self._ioucon_thread = None
self._terminate_process_iou()
if self._iou_process.returncode is None:
try:
yield from gns3server.utils.asyncio.wait_for_process_termination(self._iou_process, timeout=3)
@@ -671,7 +672,7 @@ class IOUVM(BaseVM):
netmap_path = os.path.join(self.working_dir, "NETMAP")
try:
with open(netmap_path, "w") as f:
with open(netmap_path, "w", encoding="utf-8") as f:
for bay in range(0, 16):
for unit in range(0, 4):
f.write("{iouyap_id}:{bay}/{unit}{iou_id:>5d}:{bay}/{unit}\n".format(iouyap_id=str(self.application_id + 512),
@@ -741,8 +742,8 @@ class IOUVM(BaseVM):
output = ""
if self._iou_stdout_file:
try:
with open(self._iou_stdout_file, errors="replace") as file:
output = file.read()
with open(self._iou_stdout_file, "rb") as file:
output = file.read().decode("utf-8", errors="replace")
except OSError as e:
log.warn("could not read {}: {}".format(self._iou_stdout_file, e))
return output
@@ -756,8 +757,8 @@ class IOUVM(BaseVM):
output = ""
if self._iouyap_stdout_file:
try:
with open(self._iouyap_stdout_file, errors="replace") as file:
output = file.read()
with open(self._iouyap_stdout_file, "rb") as file:
output = file.read().decode("utf-8", errors="replace")
except OSError as e:
log.warn("could not read {}: {}".format(self._iouyap_stdout_file, e))
return output
@@ -949,8 +950,8 @@ class IOUVM(BaseVM):
return None
try:
with open(config_file) as f:
return f.read()
with open(config_file, "rb") as f:
return f.read().decode("utf-8", errors="replace")
except OSError as e:
raise IOUError("Can't read configuration file '{}': {}".format(config_file, e))
@@ -963,23 +964,23 @@ class IOUVM(BaseVM):
"""
try:
script_file = os.path.join(self.working_dir, "initial-config.cfg")
initial_config_path = os.path.join(self.working_dir, "initial-config.cfg")
if initial_config is None:
initial_config = ''
# We disallow erasing the initial config file
if len(initial_config) == 0 and os.path.exists(script_file):
if len(initial_config) == 0 and os.path.exists(initial_config_path):
return
with open(script_file, 'w+') as f:
with open(initial_config_path, 'w+', encoding='utf-8') as f:
if len(initial_config) == 0:
f.write('')
else:
initial_config = initial_config.replace("%h", self._name)
f.write(initial_config)
except OSError as e:
raise IOUError("Can't write initial configuration file '{}': {}".format(script_file, e))
raise IOUError("Can't write initial configuration file '{}': {}".format(initial_config_path, e))
@property
def initial_config_file(self):
@@ -1070,6 +1071,10 @@ class IOUVM(BaseVM):
port_number=port_number))
nio = adapter.get_nio(port_number)
if not nio:
raise IOUError("NIO {port_number} does not exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
nio.stopPacketCapture()
log.info('IOU "{name}" [{id}]: stopping packet capture on {adapter_number}/{port_number}'.format(name=self._name,
id=self._id,
+1
View File
@@ -102,6 +102,7 @@ class Project:
server_config = Config.instance().get_section_config("Server")
path = os.path.expanduser(server_config.get("projects_path", "~/GNS3/projects"))
path = os.path.normpath(path)
try:
os.makedirs(path, exist_ok=True)
except OSError as e:
+12 -1
View File
@@ -30,6 +30,9 @@ from ..base_manager import BaseManager
from .qemu_error import QemuError
from .qemu_vm import QemuVM
import logging
log = logging.getLogger(__name__)
class Qemu(BaseManager):
@@ -44,7 +47,15 @@ class Qemu(BaseManager):
"""
qemus = []
paths = [os.getcwd()] + os.environ["PATH"].split(os.pathsep)
paths = []
try:
paths.append(os.getcwd())
except FileNotFoundError:
log.warning("The current working directory doesn't exist")
if "PATH" in os.environ:
paths.extend(os.environ["PATH"].split(os.pathsep))
else:
log.warning("The PATH environment variable doesn't exist")
# look for Qemu binaries in the current working directory and $PATH
if sys.platform.startswith("win"):
# add specific Windows paths
+24 -17
View File
@@ -32,6 +32,7 @@ import socket
from .qemu_error import QemuError
from ..adapters.ethernet_adapter import EthernetAdapter
from ..nios.nio_udp import NIOUDP
from ..nios.nio_tap import NIOTAP
from ..base_vm import BaseVM
from ...schemas.qemu import QEMU_OBJECT_SCHEMA
@@ -574,7 +575,7 @@ class QemuVM(BaseVM):
log.info("Starting QEMU: {}".format(self._command))
self._stdout_file = os.path.join(self.working_dir, "qemu.log")
log.info("logging to {}".format(self._stdout_file))
with open(self._stdout_file, "w") as fd:
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,
@@ -644,7 +645,7 @@ class QemuVM(BaseVM):
break
for expect in expected:
if expect in line:
result = line.decode().strip()
result = line.decode("utf-8").strip()
break
except EOFError as e:
log.warn("Could not read from QEMU monitor: {}".format(e))
@@ -817,8 +818,8 @@ class QemuVM(BaseVM):
output = ""
if self._stdout_file:
try:
with open(self._stdout_file, errors="replace") as file:
output = file.read()
with open(self._stdout_file, "rb") as file:
output = file.read().decode("utf-8", errors="replace")
except OSError as e:
log.warn("Could not read {}: {}".format(self._stdout_file, e))
return output
@@ -995,19 +996,25 @@ class QemuVM(BaseVM):
else:
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_number)])
nio = adapter.get_nio(0)
if nio and isinstance(nio, NIOUDP):
if self._legacy_networking:
network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number,
adapter_number,
nio.lport,
nio.rport,
nio.rhost)])
else:
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
nio.rhost,
nio.rport,
self._host,
nio.lport)])
if nio:
if isinstance(nio, NIOUDP):
if self._legacy_networking:
network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number,
adapter_number,
nio.lport,
nio.rport,
nio.rhost)])
else:
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
nio.rhost,
nio.rport,
self._host,
nio.lport)])
elif isinstance(nio, NIOTAP):
if self._legacy_networking:
network_options.extend(["-net", "tap,name=gns3-{},ifname={}".format(adapter_number, nio.tap_device)])
else:
network_options.extend(["-netdev", "tap,id=gns3-{},ifname={}".format(adapter_number, nio.tap_device)])
else:
if self._legacy_networking:
network_options.extend(["-net", "user,vlan={},name=gns3-{}".format(adapter_number, adapter_number)])
+1 -1
View File
@@ -110,7 +110,6 @@ class VirtualBox(BaseManager):
raise VirtualBoxError("VBoxManage has timed out after {} seconds!".format(timeout))
if process.returncode:
# only the first line of the output is useful
vboxmanage_error = stderr_data.decode("utf-8", errors="ignore")
raise VirtualBoxError("VirtualBox has returned an error: {}".format(vboxmanage_error))
@@ -133,6 +132,7 @@ class VirtualBox(BaseManager):
if not extra_data[0].strip() == "Value: yes":
# get the amount of RAM
info_results = yield from self.execute("showvminfo", [vmname, "--machinereadable"])
ram = 0
for info in info_results:
try:
name, value = info.split('=', 1)
+17 -7
View File
@@ -209,7 +209,7 @@ class VirtualBoxVM(BaseVM):
log.info("VirtualBox VM '{name}' [{id}] stopped".format(name=self.name, id=self.id))
log.debug("Stop result: {}".format(result))
yield from asyncio.sleep(0.5) # give some time for VirtualBox to unlock the VM
# yield from asyncio.sleep(0.5) # give some time for VirtualBox to unlock the VM
try:
# deactivate the first serial port
yield from self._modify_vm("--uart1 off")
@@ -276,7 +276,7 @@ class VirtualBoxVM(BaseVM):
hdd_info_file = os.path.join(self.working_dir, self._vmname, "hdd_info.json")
try:
with open(hdd_info_file, "r") as f:
with open(hdd_info_file, "r", encoding="utf-8") as f:
hdd_table = json.load(f)
except OSError as e:
raise VirtualBoxError("Could not read HDD info file: {}".format(e))
@@ -321,6 +321,7 @@ class VirtualBoxVM(BaseVM):
if self._linked_clone:
hdd_table = []
hdd_files_to_close = []
if os.path.exists(self.working_dir):
hdd_files = yield from self._get_all_hdd_files()
vm_info = yield from self._get_vm_info()
@@ -331,6 +332,7 @@ class VirtualBoxVM(BaseVM):
port = match.group(2)
device = match.group(3)
if value in hdd_files:
hdd_files_to_close.append(value)
log.info("VirtualBox VM '{name}' [{id}] detaching HDD {controller} {port} {device}".format(name=self.name,
id=self.id,
controller=controller,
@@ -351,10 +353,16 @@ class VirtualBoxVM(BaseVM):
log.info("VirtualBox VM '{name}' [{id}] unregistering".format(name=self.name, id=self.id))
yield from self.manager.execute("unregistervm", [self._name])
for hdd_file in hdd_files_to_close:
log.info("VirtualBox VM '{name}' [{id}] closing disk {disk}".format(name=self.name,
id=self.id,
disk=os.path.basename(hdd_file)))
yield from self.manager.execute("closemedium", ["disk", hdd_file])
if hdd_table:
try:
hdd_info_file = os.path.join(self.working_dir, self._vmname, "hdd_info.json")
with open(hdd_info_file, "w") as f:
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,
@@ -586,12 +594,14 @@ class VirtualBoxVM(BaseVM):
:returns: pipe path (string)
"""
p = re.compile('\s+', re.UNICODE)
pipe_name = p.sub("_", self._vmname)
if sys.platform.startswith("win"):
pipe_name = r"\\.\pipe\VBOX\{}".format(pipe_name)
pipe_name = r"\\.\pipe\gns3_vbox\{}".format(self.id)
else:
pipe_name = os.path.join(tempfile.gettempdir(), "pipe_{}".format(pipe_name))
pipe_name = os.path.join(tempfile.gettempdir(), "gns3_vbox", "{}".format(self.id))
try:
os.makedirs(os.path.dirname(pipe_name), exist_ok=True)
except OSError as e:
raise VirtualBoxError("Could not create the VirtualBox pipe directory: {}".format(e))
return pipe_name
@asyncio.coroutine
+17 -15
View File
@@ -27,6 +27,7 @@ import signal
import re
import asyncio
import shutil
import gns3server.utils.asyncio
from pkg_resources import parse_version
from .vpcs_error import VPCSError
@@ -166,8 +167,8 @@ class VPCSVM(BaseVM):
return None
try:
with open(script_file) as f:
return f.read()
with open(script_file, "rb") as f:
return f.read().decode("utf-8", errors="replace")
except OSError as e:
raise VPCSError('Cannot read the startup script file "{}": {}'.format(script_file, e))
@@ -181,12 +182,12 @@ class VPCSVM(BaseVM):
try:
script_file = os.path.join(self.working_dir, 'startup.vpc')
with open(script_file, 'w+') as f:
with open(script_file, "wb+") as f:
if startup_script is None:
f.write('')
f.write(b'')
else:
startup_script = startup_script.replace("%h", self._name)
f.write(startup_script)
f.write(startup_script.encode("utf-8"))
except OSError as e:
raise VPCSError('Cannot write the startup script file "{}": {}'.format(self.script_file, e))
@@ -226,7 +227,7 @@ class VPCSVM(BaseVM):
flags = 0
if sys.platform.startswith("win32"):
flags = subprocess.CREATE_NEW_PROCESS_GROUP
with open(self._vpcs_stdout_file, "w") as fd:
with open(self._vpcs_stdout_file, "w", encoding="utf-8") as fd:
self._process = yield from asyncio.create_subprocess_exec(*self._command,
stdout=fd,
stderr=subprocess.STDOUT,
@@ -247,12 +248,13 @@ class VPCSVM(BaseVM):
if self.is_running():
self._terminate_process()
try:
yield from asyncio.wait_for(self._process.wait(), timeout=3)
except asyncio.TimeoutError:
if self._process.returncode is None:
log.warn("VPCS process {} is still running... killing it".format(self._process.pid))
self._process.kill()
if self._process.returncode is None:
try:
yield from gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=3)
except asyncio.TimeoutError:
if self._process.returncode is None:
log.warn("VPCS process {} is still running... killing it".format(self._process.pid))
self._process.kill()
self._process = None
self._started = False
@@ -290,8 +292,8 @@ class VPCSVM(BaseVM):
output = ""
if self._vpcs_stdout_file:
try:
with open(self._vpcs_stdout_file, errors="replace") as file:
output = file.read()
with open(self._vpcs_stdout_file, "rb") as file:
output = file.read().decode("utf-8", errors="replace")
except OSError as e:
log.warn("Could not read {}: {}".format(self._vpcs_stdout_file, e))
return output
@@ -407,7 +409,7 @@ class VPCSVM(BaseVM):
command.extend(["-F"]) # option to avoid the daemonization of VPCS
if self.script_file:
command.extend([self.script_file])
command.extend([os.path.basename(self.script_file)])
return command
@property