Claudiu Popa 0b40f6368b Provide the error code in the Windows exception messages
This patch adds a type of exception for the Windows code, which interpolates
the given exception message with the last Windows API error, retrieved with
ctypes.GetLastError. This is useful in the case where we have only the log
at our disposal for debugging and some API method failed with reasons unknown.
Since we can't replicate what the user does everytime, having some additional
clue why an API failed could improve our bug detection workflow.

Change-Id: I364324ad5a8529b5363be3a7c6dc03ca52eb637c
2015-03-16 16:51:40 +02:00

264 lines
10 KiB
Python

# Copyright 2014 Cloudbase Solutions Srl
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import importlib
try:
import unittest.mock as mock
except ImportError:
import mock
from cloudbaseinit.tests import testutils
class WindowsVirtualDiskUtilsTests(testutils.CloudbaseInitTestBase):
def setUp(self):
self._ctypes_mock = mock.MagicMock()
self._module_patcher = mock.patch.dict(
'sys.modules',
{'ctypes': self._ctypes_mock})
self._module_patcher.start()
self.virtual_disk = importlib.import_module(
"cloudbaseinit.utils.windows.virtual_disk")
self.fake_path = mock.sentinel.fake_path
self._vdisk_class = self.virtual_disk.VirtualDisk(path=self.fake_path)
self.virtual_disk.virtdisk = None
self.virtual_disk.kernel32 = mock.MagicMock()
def tearDown(self):
self._module_patcher.stop()
def test_load_virtdisk_dll(self):
self._vdisk_class._load_virtdisk_dll()
self.assertEqual(self._ctypes_mock.windll.virtdisk,
self.virtual_disk.virtdisk)
@mock.patch('cloudbaseinit.utils.windows.virtual_disk'
'.Win32_VIRTUAL_STORAGE_TYPE')
@mock.patch('cloudbaseinit.utils.windows.virtual_disk'
'.get_WIN32_VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT')
@mock.patch('cloudbaseinit.utils.windows.virtual_disk'
'.VirtualDisk._load_virtdisk_dll')
@mock.patch('cloudbaseinit.utils.windows.virtual_disk'
'.VirtualDisk.close')
def _test_open(self, mock_close, mock_load_virtdisk_dll,
mock_get_virtual_storage_type_vendor,
mock_Win32_VIRTUAL_STORAGE_TYPE, handle, ret_val):
virtdisk = self._ctypes_mock.windll.virtdisk
virtdisk.OpenVirtualDisk.return_value = ret_val
self.virtual_disk.virtdisk = virtdisk
self._vdisk_class._handle = None
if handle:
self._vdisk_class._handle = handle
if ret_val:
with self.assert_raises_windows_message(
"Cannot open virtual disk: %r",
ret_val):
self._vdisk_class.open()
else:
self._vdisk_class.open()
if handle:
mock_close.assert_called_once_with()
mock_load_virtdisk_dll.assert_called_once_with()
mock_Win32_VIRTUAL_STORAGE_TYPE.assert_called_once_with()
mock_get_virtual_storage_type_vendor.assert_called_once_with()
self.assertEqual(
self._vdisk_class.VIRTUAL_STORAGE_TYPE_DEVICE_ISO,
mock_Win32_VIRTUAL_STORAGE_TYPE.return_value.DeviceId)
self.assertEqual(self._ctypes_mock.wintypes.HANDLE.return_value,
self._vdisk_class._handle)
def test_open(self):
self._test_open(handle=None, ret_val=None)
def test_open_exception(self):
self._test_open(handle=None, ret_val=100)
def test_open_handle_exists(self):
self._test_open(handle=None, ret_val=None)
def _test_attach(self, ret_val):
virtdisk = self._ctypes_mock.windll.virtdisk
self.virtual_disk.virtdisk = virtdisk
virtdisk.AttachVirtualDisk.return_value = ret_val
if ret_val:
with self.assert_raises_windows_message(
"Cannot attach virtual disk: %r",
ret_val):
self._vdisk_class.attach()
else:
self._vdisk_class.attach()
virtdisk.AttachVirtualDisk.assert_called_once_with(
self._vdisk_class._handle, 0,
self._vdisk_class.ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY, 0, 0, 0)
def test_attach(self):
self._test_attach(ret_val=None)
def test_attach_exception(self):
self._test_attach(ret_val=100)
def _test_detach(self, ret_val):
virtdisk = self._ctypes_mock.windll.virtdisk
self.virtual_disk.virtdisk = virtdisk
virtdisk.DetachVirtualDisk.return_value = ret_val
if ret_val:
with self.assert_raises_windows_message(
"Cannot detach virtual disk: %r", ret_val):
self._vdisk_class.detach()
else:
self._vdisk_class.detach()
virtdisk.DetachVirtualDisk.assert_called_once_with(
self._vdisk_class._handle,
self._vdisk_class.DETACH_VIRTUAL_DISK_FLAG_NONE, 0)
def test_detach(self):
self._test_detach(ret_val=None)
def test_detach_exception(self):
self._test_detach(ret_val=100)
def _test_get_physical_path(self, ret_val):
virtdisk = self._ctypes_mock.windll.virtdisk
self.virtual_disk.virtdisk = virtdisk
virtdisk.GetVirtualDiskPhysicalPath.return_value = ret_val
buf = self._ctypes_mock.create_unicode_buffer.return_value
if ret_val:
with self.assert_raises_windows_message(
"Cannot get virtual disk physical path: %r", ret_val):
self._vdisk_class.get_physical_path()
else:
response = self._vdisk_class.get_physical_path()
self.assertEqual(buf.value, response)
self._ctypes_mock.create_unicode_buffer.assert_called_once_with(1024)
self._ctypes_mock.wintypes.DWORD.assert_called_once_with(
self._ctypes_mock.sizeof.return_value)
self._ctypes_mock.sizeof.assert_called_once_with(
buf)
virtdisk.GetVirtualDiskPhysicalPath.assert_called_once_with(
self._vdisk_class._handle,
self._ctypes_mock.byref.return_value,
self._ctypes_mock.create_unicode_buffer.return_value)
self._ctypes_mock.byref.assert_called_once_with(
self._ctypes_mock.wintypes.DWORD.return_value)
self._ctypes_mock.create_unicode_buffer.assert_called_once_with(1024)
def test_get_physical_path(self):
self._test_get_physical_path(ret_val=None)
def test_get_physical_path_fails(self):
self._test_get_physical_path(ret_val=100)
@mock.patch('cloudbaseinit.utils.windows.virtual_disk'
'.VirtualDisk.get_physical_path')
def _test_get_cdrom_drive_mount_point(self, mock_get_physical_path,
buf_len, ret_val, last_error=None):
buf = self._ctypes_mock.create_unicode_buffer.return_value
kernel32 = self.virtual_disk.kernel32
kernel32.GetLogicalDriveStringsW.return_value = buf_len
kernel32.QueryDosDeviceW.return_value = ret_val
self._ctypes_mock.wstring_at.return_value = [mock.sentinel.value1,
mock.sentinel.value2]
dev = self._ctypes_mock.create_unicode_buffer.return_value
dev.value = mock_get_physical_path.return_value
self._ctypes_mock.sizeof.return_value = 1
expected_sizeof = [mock.call(buf),
mock.call(self._ctypes_mock.wintypes.WCHAR)]
expected_create_unicode_buffer = [mock.call(2048)]
if not buf_len:
with self.assert_raises_windows_message(
"Cannot enumerate logical devices: %r", last_error):
self._vdisk_class.get_cdrom_drive_mount_point()
elif not ret_val:
with self.assert_raises_windows_message(
"Cannot query NT device: %r", last_error):
self._vdisk_class.get_cdrom_drive_mount_point()
expected_create_unicode_buffer.append(mock.call(2048))
expected_sizeof.append(mock.call(self._ctypes_mock.wintypes.WCHAR))
expected_sizeof.append(
mock.call(
self._ctypes_mock.create_unicode_buffer.return_value))
expected_sizeof.append(mock.call(self._ctypes_mock.wintypes.WCHAR))
else:
response = self._vdisk_class.get_cdrom_drive_mount_point()
mock_get_physical_path.assert_called_once_with()
self._ctypes_mock.wstring_at.assert_called_once_with(
self._ctypes_mock.addressof.return_value + 0 * 1)
self._ctypes_mock.addressof.assert_called_once_with(buf)
kernel32.QueryDosDeviceW.assert_called_once_with(
[mock.sentinel.value1],
self._ctypes_mock.create_unicode_buffer.return_value, 1)
expected_sizeof.append(mock.call(self._ctypes_mock.wintypes.WCHAR))
expected_sizeof.append(
mock.call(
self._ctypes_mock.create_unicode_buffer.return_value))
expected_sizeof.append(mock.call(self._ctypes_mock.wintypes.WCHAR))
expected_create_unicode_buffer.append(mock.call(2048))
self.assertEqual(self._ctypes_mock.wstring_at.return_value[:-1],
response)
self.assertEqual(
expected_create_unicode_buffer,
self._ctypes_mock.create_unicode_buffer.call_args_list)
self.assertEqual(expected_sizeof,
self._ctypes_mock.sizeof.call_args_list)
kernel32.GetLogicalDriveStringsW.assert_called_once_with(1, buf)
def test_get_cdrom_drive_mount_point_exception_buf_len(self):
self._test_get_cdrom_drive_mount_point(buf_len=0, ret_val=1,
last_error=100)
def test_get_cdrom_drive_mount_point_exception_query(self):
self._test_get_cdrom_drive_mount_point(buf_len=1, ret_val=0,
last_error=100)
def test_get_cdrom_drive_mount_point(self):
self._test_get_cdrom_drive_mount_point(buf_len=1, ret_val=1)
def test_close(self):
self._vdisk_class.close()
self.virtual_disk.kernel32.CloseHandle.assert_called_once_with(
self._vdisk_class._handle)
self.assertEqual(0, self._vdisk_class._handle)