Merge branch 'master' into 1.5

This commit is contained in:
Julien Duponchelle 2016-04-28 16:52:29 +02:00
commit 815ef6066d
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
2 changed files with 162 additions and 56 deletions

View File

@ -158,7 +158,7 @@ class VirtualBoxVM(BaseVM):
if self.id and os.path.isdir(os.path.join(self.working_dir, self._vmname)): 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") vbox_file = os.path.join(self.working_dir, self._vmname, self._vmname + ".vbox")
yield from self.manager.execute("registervm", [vbox_file]) yield from self.manager.execute("registervm", [vbox_file])
yield from self._reattach_hdds() yield from self._reattach_linked_hdds()
else: else:
yield from self._create_linked_clone() yield from self._create_linked_clone()
@ -313,7 +313,10 @@ class VirtualBoxVM(BaseVM):
return hdds return hdds
@asyncio.coroutine @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") hdd_info_file = os.path.join(self.working_dir, self._vmname, "hdd_info.json")
try: try:
@ -332,10 +335,67 @@ class VirtualBoxVM(BaseVM):
device=hdd_info["device"], device=hdd_info["device"],
medium=hdd_file)) medium=hdd_file))
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium "{}"'.format(hdd_info["controller"], try:
hdd_info["port"], yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium "{}"'.format(hdd_info["controller"],
hdd_info["device"], hdd_info["port"],
hdd_file)) 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 @asyncio.coroutine
def close(self): def close(self):
@ -343,9 +403,18 @@ class VirtualBoxVM(BaseVM):
Closes this VirtualBox VM. Closes this VirtualBox VM.
""" """
if self._closed:
# VM is already closed
return
if not (yield from super().close()): if not (yield from super().close()):
return False return False
log.debug("VirtualBox VM '{name}' [{id}] is closing".format(name=self.name, id=self.id))
if self._console:
self._manager.port_manager.release_tcp_port(self._console, self._project)
self._console = None
for adapter in self._ethernet_adapters.values(): for adapter in self._ethernet_adapters.values():
if adapter is not None: if adapter is not None:
for nio in adapter.ports.values(): for nio in adapter.ports.values():
@ -356,46 +425,31 @@ class VirtualBoxVM(BaseVM):
yield from self.stop() yield from self.stop()
if self._linked_clone: if self._linked_clone:
hdd_table = [] hdd_table = yield from self.save_linked_hdds_info()
if os.path.exists(self.working_dir): for hdd in hdd_table.copy():
hdd_files = yield from self._get_all_hdd_files() log.info("VirtualBox VM '{name}' [{id}] detaching HDD {controller} {port} {device}".format(name=self.name,
vm_info = yield from self._get_vm_info() id=self.id,
for entry, value in vm_info.items(): controller=hdd["controller"],
match = re.search("^([\s\w]+)\-(\d)\-(\d)$", entry) port=hdd["port"],
if match: device=hdd["device"]))
controller = match.group(1) try:
port = match.group(2) yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium none'.format(hdd["controller"],
device = match.group(3) hdd["port"],
if value in hdd_files: hdd["device"]))
log.info("VirtualBox VM '{name}' [{id}] detaching HDD {controller} {port} {device}".format(name=self.name, except VirtualBoxError as e:
id=self.id, log.warn("VirtualBox VM '{name}' [{id}] error detaching HDD {controller} {port} {device}: {error}".format(name=self.name,
controller=controller, id=self.id,
port=port, controller=hdd["controller"],
device=device)) port=hdd["port"],
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium none'.format(controller, device=hdd["device"],
port, error=e))
device)) continue
hdd_table.append(
{
"hdd": os.path.basename(value),
"controller": controller,
"port": port,
"device": device,
}
)
log.info("VirtualBox VM '{name}' [{id}] unregistering".format(name=self.name, id=self.id)) log.info("VirtualBox VM '{name}' [{id}] unregistering".format(name=self.name, id=self.id))
yield from self.manager.execute("unregistervm", [self._name]) yield from self.manager.execute("unregistervm", [self._name])
if hdd_table: log.info("VirtualBox VM '{name}' [{id}] closed".format(name=self.name, id=self.id))
try: self._closed = True
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))
@property @property
def headless(self): def headless(self):

View File

@ -108,7 +108,7 @@ class VMware(BaseManager):
vmrun_path = shutil.which("vmrun") vmrun_path = shutil.which("vmrun")
if not vmrun_path: 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): if not os.path.isfile(vmrun_path):
raise VMwareError("vmrun {} is not accessible".format(vmrun_path)) raise VMwareError("vmrun {} is not accessible".format(vmrun_path))
if not os.access(vmrun_path, os.X_OK): if not os.access(vmrun_path, os.X_OK):
@ -137,6 +137,50 @@ class VMware(BaseManager):
version = match.group(1) version = match.group(1)
return version 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 @asyncio.coroutine
def check_vmware_version(self): def check_vmware_version(self):
""" """
@ -150,15 +194,12 @@ class VMware(BaseManager):
player_version = self._find_vmware_version_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Player") player_version = self._find_vmware_version_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Player")
if player_version: if player_version:
log.debug("VMware Player version {} detected".format(player_version)) log.debug("VMware Player version {} detected".format(player_version))
if int(player_version) < 6: yield from self._check_vmware_player_requirements(player_version)
raise VMwareError("Using VMware Player requires version 6 or above")
else: else:
log.warning("Could not find VMware version") log.warning("Could not find VMware version")
else: else:
log.debug("VMware Workstation version {} detected".format(ws_version)) log.debug("VMware Workstation version {} detected".format(ws_version))
if int(ws_version) < 10: yield from self._check_vmware_workstation_requirements(ws_version)
raise VMwareError("Using VMware Workstation requires version 10 or above")
return
else: else:
if sys.platform.startswith("darwin"): if sys.platform.startswith("darwin"):
if not os.path.isdir("/Applications/VMware Fusion.app"): if not os.path.isdir("/Applications/VMware Fusion.app"):
@ -174,16 +215,16 @@ class VMware(BaseManager):
match = re.search("VMware Workstation ([0-9]+)\.", output) match = re.search("VMware Workstation ([0-9]+)\.", output)
version = None version = None
if match: if match:
# VMware Workstation has been detected
version = match.group(1) version = match.group(1)
log.debug("VMware Workstation version {} detected".format(version)) log.debug("VMware Workstation version {} detected".format(version))
if int(version) < 10: yield from self._check_vmware_workstation_requirements(version)
raise VMwareError("Using VMware Workstation requires version 10 or above")
match = re.search("VMware Player ([0-9]+)\.", output) match = re.search("VMware Player ([0-9]+)\.", output)
if match: if match:
# VMware Player has been detected
version = match.group(1) version = match.group(1)
log.debug("VMware Player version {} detected".format(version)) log.debug("VMware Player version {} detected".format(version))
if int(version) < 6: yield from self._check_vmware_player_requirements(version)
raise VMwareError("Using VMware Player requires version 6 or above")
if version is None: if version is None:
log.warning("Could not find VMware version. Output of VMware: {}".format(output)) log.warning("Could not find VMware version. Output of VMware: {}".format(output))
raise VMwareError("Could not find VMware version. Output of VMware: {}".format(output)) raise VMwareError("Could not find VMware version. Output of VMware: {}".format(output))
@ -352,7 +393,17 @@ class VMware(BaseManager):
return stdout_data.decode("utf-8", errors="ignore").splitlines() return stdout_data.decode("utf-8", errors="ignore").splitlines()
@asyncio.coroutine @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): with (yield from self._execute_lock):
vmrun_path = self.vmrun_path vmrun_path = self.vmrun_path
@ -366,9 +417,9 @@ class VMware(BaseManager):
if match: if match:
version = match.group(1) version = match.group(1)
log.debug("VMware vmrun version {} detected".format(version)) log.debug("VMware vmrun version {} detected".format(version))
if parse_version(version) < parse_version("1.13"): if parse_version(version) < parse_version(minimum_required_version):
# VMware VIX library version must be at least >= 1.13
raise VMwareError("VMware vmrun executable version must be >= version 1.13") raise VMwareError("VMware vmrun executable version must be >= version {}".format(minimum_required_version))
if version is None: if version is None:
log.warning("Could not find VMware vmrun version. Output: {}".format(output)) log.warning("Could not find VMware vmrun version. Output: {}".format(output))
raise VMwareError("Could not find VMware vmrun version. Output: {}".format(output)) raise VMwareError("Could not find VMware vmrun version. Output: {}".format(output))
@ -595,6 +646,7 @@ class VMware(BaseManager):
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
import ctypes import ctypes
import ctypes.wintypes
path = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) path = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
ctypes.windll.shell32.SHGetFolderPathW(None, 5, None, 0, path) ctypes.windll.shell32.SHGetFolderPathW(None, 5, None, 0, path)
documents_folder = path.value documents_folder = path.value