Add openvixdisklib as an open NBD replacement for VMware VDDK.
VDDK is no longer publicly distributed, so this library reverse-engineers the vSphere NFC path and exposes ConnectEx, Open, Read, and Write without the proprietary SDK. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -0,0 +1,336 @@
|
||||
# Copyright 2026 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
|
||||
"""Test base classes for openvixdisklib integration tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
import uuid
|
||||
from typing import Any, Optional
|
||||
|
||||
import yaml
|
||||
from pyVim.connect import Disconnect
|
||||
from pyVmomi import vim
|
||||
|
||||
from openvixdisklib import nfc_auth
|
||||
from openvixdisklib.nfc_auth import NfcAuthSession
|
||||
|
||||
_REPO_ROOT = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
_CONFIG_PATH = os.path.join(_REPO_ROOT, ".test_config.yaml")
|
||||
_CONFIG_KEYS = (
|
||||
"host",
|
||||
"port",
|
||||
"username",
|
||||
"password",
|
||||
"allow_untrusted",
|
||||
"datacenter",
|
||||
"datastore",
|
||||
)
|
||||
_VDDK_DIR = os.path.join(_REPO_ROOT, ".vddk")
|
||||
_VDDK_LIB = os.path.join(_VDDK_DIR, "libvixDiskLib.so")
|
||||
_DISK_CAPACITY_KB = 10 * 1024 * 1024
|
||||
_TASK_POLL_S = 0.5
|
||||
_TASK_TIMEOUT_S = 300
|
||||
_LAB_VM_PREFIX = "ovdl-test-"
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
"""Shared lab vSphere settings for live NFC / VDDK integration tests."""
|
||||
|
||||
HOST: str
|
||||
PORT: int
|
||||
USERNAME: str
|
||||
PASSWORD: str
|
||||
ALLOW_UNTRUSTED: bool
|
||||
DATACENTER: str
|
||||
DATASTORE: str
|
||||
THUMBPRINT: str
|
||||
VM_MOREF: str
|
||||
VMX_SPEC: str
|
||||
DISK_PATH: str
|
||||
SECTOR_SIZE = 512
|
||||
SECTOR_AT_1GB = (1024 * 1024 * 1024) // SECTOR_SIZE
|
||||
VDDK_DIR = _VDDK_DIR
|
||||
_lab_refcount = 0
|
||||
_lab_vm_moref: Optional[str] = None
|
||||
_lab_vm_name: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
"""Prepare process environment and create a temporary lab VM."""
|
||||
super().setUpClass()
|
||||
os.environ.pop("LD_PRELOAD", None)
|
||||
cls._ensure_vddk_library_path()
|
||||
TestBase._acquire_lab()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
"""Release the temporary lab VM when the last test class finishes."""
|
||||
TestBase._release_lab()
|
||||
super().tearDownClass()
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Reset per-test state; subclasses may reuse this."""
|
||||
super().setUp()
|
||||
|
||||
@classmethod
|
||||
def _ensure_vddk_library_path(cls) -> None:
|
||||
current = os.environ.get("LD_LIBRARY_PATH", "")
|
||||
parts = [p for p in current.split(":") if p]
|
||||
if cls.VDDK_DIR not in parts:
|
||||
os.environ["LD_LIBRARY_PATH"] = (
|
||||
cls.VDDK_DIR if not current else f"{cls.VDDK_DIR}:{current}")
|
||||
|
||||
@classmethod
|
||||
def require_vddk(cls) -> None:
|
||||
"""Skip when ``libvixDiskLib`` cannot be loaded from ``.vddk``."""
|
||||
cls._ensure_vddk_library_path()
|
||||
try:
|
||||
ctypes.CDLL(_VDDK_LIB)
|
||||
except OSError as exc:
|
||||
raise unittest.SkipTest(
|
||||
f"VDDK library not available at {_VDDK_LIB}: {exc}") from exc
|
||||
|
||||
@classmethod
|
||||
def _load_test_config(cls) -> None:
|
||||
"""Load lab settings from the repo-root ``.test_config.yaml``."""
|
||||
if not os.path.isfile(_CONFIG_PATH):
|
||||
raise unittest.SkipTest(
|
||||
"integration tests need .test_config.yaml in the repo "
|
||||
"root; see README.md for a sample")
|
||||
with open(_CONFIG_PATH, encoding="utf-8") as config_file:
|
||||
data = yaml.safe_load(config_file) or {}
|
||||
missing = [key for key in _CONFIG_KEYS if key not in data]
|
||||
if missing:
|
||||
raise RuntimeError(
|
||||
f"{_CONFIG_PATH} is missing keys: {', '.join(missing)}")
|
||||
TestBase.HOST = str(data["host"])
|
||||
TestBase.PORT = int(data["port"])
|
||||
TestBase.USERNAME = str(data["username"])
|
||||
TestBase.PASSWORD = str(data["password"])
|
||||
TestBase.ALLOW_UNTRUSTED = bool(data["allow_untrusted"])
|
||||
TestBase.DATACENTER = str(data["datacenter"])
|
||||
TestBase.DATASTORE = str(data["datastore"])
|
||||
|
||||
@classmethod
|
||||
def _connect_vim(cls) -> vim.ServiceInstance:
|
||||
return nfc_auth.connect_vim(
|
||||
cls.HOST,
|
||||
cls.USERNAME,
|
||||
cls.PASSWORD,
|
||||
port=cls.PORT,
|
||||
thumbprint=cls.THUMBPRINT,
|
||||
allow_untrusted=cls.ALLOW_UNTRUSTED)
|
||||
|
||||
@classmethod
|
||||
def _wait_for_task(cls, task: vim.Task) -> Any:
|
||||
deadline = time.monotonic() + _TASK_TIMEOUT_S
|
||||
while task.info.state in (
|
||||
vim.TaskInfo.State.running, vim.TaskInfo.State.queued):
|
||||
if time.monotonic() > deadline:
|
||||
raise TimeoutError(
|
||||
f"timed out waiting for vSphere task {task}")
|
||||
time.sleep(_TASK_POLL_S)
|
||||
if task.info.state != vim.TaskInfo.State.success:
|
||||
raise RuntimeError(f"vSphere task failed: {task.info.error}")
|
||||
return task.info.result
|
||||
|
||||
@classmethod
|
||||
def _find_datacenter(
|
||||
cls, content: vim.ServiceInstanceContent) -> vim.Datacenter:
|
||||
matches = [
|
||||
entity for entity in content.rootFolder.childEntity
|
||||
if isinstance(entity, vim.Datacenter)
|
||||
and entity.name == cls.DATACENTER]
|
||||
if not matches:
|
||||
raise RuntimeError(f"datacenter {cls.DATACENTER!r} not found")
|
||||
return matches[0]
|
||||
|
||||
@classmethod
|
||||
def _find_datastore(cls, datacenter: vim.Datacenter) -> vim.Datastore:
|
||||
matches = [
|
||||
datastore for datastore in datacenter.datastore
|
||||
if datastore.name == cls.DATASTORE]
|
||||
if not matches:
|
||||
raise RuntimeError(
|
||||
f"datastore {cls.DATASTORE!r} not found in "
|
||||
f"datacenter {cls.DATACENTER!r}")
|
||||
return matches[0]
|
||||
|
||||
@classmethod
|
||||
def _bind_lab_fields(cls) -> None:
|
||||
"""Copy shared lab VM fields onto the active test class."""
|
||||
cls.HOST = TestBase.HOST
|
||||
cls.PORT = TestBase.PORT
|
||||
cls.USERNAME = TestBase.USERNAME
|
||||
cls.PASSWORD = TestBase.PASSWORD
|
||||
cls.ALLOW_UNTRUSTED = TestBase.ALLOW_UNTRUSTED
|
||||
cls.DATACENTER = TestBase.DATACENTER
|
||||
cls.DATASTORE = TestBase.DATASTORE
|
||||
cls.THUMBPRINT = TestBase.THUMBPRINT
|
||||
cls.VM_MOREF = TestBase.VM_MOREF
|
||||
cls.VMX_SPEC = TestBase.VMX_SPEC
|
||||
cls.DISK_PATH = TestBase.DISK_PATH
|
||||
|
||||
@classmethod
|
||||
def _acquire_lab(cls) -> None:
|
||||
if TestBase._lab_refcount == 0:
|
||||
TestBase._load_test_config()
|
||||
TestBase.THUMBPRINT = nfc_auth.get_ssl_cert_thumbprint(
|
||||
TestBase.HOST, TestBase.PORT)
|
||||
TestBase._create_lab_vm()
|
||||
TestBase._lab_refcount += 1
|
||||
cls._bind_lab_fields()
|
||||
|
||||
@classmethod
|
||||
def _release_lab(cls) -> None:
|
||||
if TestBase._lab_refcount == 0:
|
||||
return
|
||||
TestBase._lab_refcount -= 1
|
||||
if TestBase._lab_refcount == 0:
|
||||
cls._destroy_lab_vm()
|
||||
|
||||
@classmethod
|
||||
def _create_lab_vm(cls) -> None:
|
||||
"""Create an empty VM with a 10 GiB thin disk for I/O tests."""
|
||||
si = cls._connect_vim()
|
||||
vm = None
|
||||
try:
|
||||
content = si.RetrieveContent()
|
||||
datacenter = cls._find_datacenter(content)
|
||||
datastore = cls._find_datastore(datacenter)
|
||||
if not datastore.host:
|
||||
raise RuntimeError(
|
||||
f"datastore {cls.DATASTORE!r} is not mounted on any host")
|
||||
host = datastore.host[0].key
|
||||
pool = host.parent.resourcePool
|
||||
vm_name = _LAB_VM_PREFIX + uuid.uuid4().hex[:12]
|
||||
vm = cls._wait_for_task(
|
||||
datacenter.vmFolder.CreateVM_Task(
|
||||
config=cls._vm_config_spec(vm_name, datastore.name),
|
||||
pool=pool,
|
||||
host=host))
|
||||
TestBase._lab_vm_moref = vm._moId
|
||||
TestBase._lab_vm_name = vm_name
|
||||
TestBase.VM_MOREF = vm._moId
|
||||
TestBase.VMX_SPEC = f"moref={vm._moId}"
|
||||
disks = [
|
||||
device.backing.fileName
|
||||
for device in vm.config.hardware.device
|
||||
if isinstance(device, vim.vm.device.VirtualDisk)]
|
||||
if not disks:
|
||||
raise RuntimeError(
|
||||
f"temporary VM {vm_name!r} has no virtual disks")
|
||||
TestBase.DISK_PATH = disks[0]
|
||||
except Exception:
|
||||
if vm is not None:
|
||||
try:
|
||||
cls._wait_for_task(vm.Destroy_Task())
|
||||
except Exception:
|
||||
pass
|
||||
TestBase._lab_vm_moref = None
|
||||
TestBase._lab_vm_name = None
|
||||
raise
|
||||
finally:
|
||||
Disconnect(si)
|
||||
|
||||
@classmethod
|
||||
def _vm_config_spec(
|
||||
cls, vm_name: str, datastore_name: str) -> vim.vm.ConfigSpec:
|
||||
config = vim.vm.ConfigSpec()
|
||||
config.name = vm_name
|
||||
config.guestId = "otherGuest64"
|
||||
config.memoryMB = 128
|
||||
config.numCPUs = 1
|
||||
config.files = vim.vm.FileInfo(
|
||||
vmPathName=f"[{datastore_name}]")
|
||||
|
||||
controller = vim.vm.device.ParaVirtualSCSIController()
|
||||
controller.key = 1000
|
||||
controller.busNumber = 0
|
||||
controller.sharedBus = (
|
||||
vim.vm.device.VirtualSCSIController.Sharing.noSharing)
|
||||
controller_spec = vim.vm.device.VirtualDeviceSpec()
|
||||
controller_spec.operation = (
|
||||
vim.vm.device.VirtualDeviceSpec.Operation.add)
|
||||
controller_spec.device = controller
|
||||
|
||||
backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
|
||||
backing.diskMode = "persistent"
|
||||
backing.thinProvisioned = True
|
||||
backing.fileName = f"[{datastore_name}]"
|
||||
disk = vim.vm.device.VirtualDisk()
|
||||
disk.key = 2000
|
||||
disk.controllerKey = 1000
|
||||
disk.unitNumber = 0
|
||||
disk.capacityInKB = _DISK_CAPACITY_KB
|
||||
disk.backing = backing
|
||||
disk_spec = vim.vm.device.VirtualDeviceSpec()
|
||||
disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
|
||||
disk_spec.fileOperation = (
|
||||
vim.vm.device.VirtualDeviceSpec.FileOperation.create)
|
||||
disk_spec.device = disk
|
||||
|
||||
config.deviceChange = [controller_spec, disk_spec]
|
||||
return config
|
||||
|
||||
@classmethod
|
||||
def _destroy_lab_vm(cls) -> None:
|
||||
"""Power off and delete the temporary lab VM if it still exists."""
|
||||
moref = TestBase._lab_vm_moref
|
||||
TestBase._lab_vm_moref = None
|
||||
TestBase._lab_vm_name = None
|
||||
if not moref:
|
||||
return
|
||||
si = cls._connect_vim()
|
||||
try:
|
||||
vm = vim.VirtualMachine(moref, si._stub)
|
||||
try:
|
||||
vm.Reload()
|
||||
except Exception:
|
||||
return
|
||||
if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
|
||||
cls._wait_for_task(vm.PowerOffVM_Task())
|
||||
cls._wait_for_task(vm.Destroy_Task())
|
||||
finally:
|
||||
Disconnect(si)
|
||||
|
||||
def authenticate(self, read_only: bool = True) -> NfcAuthSession:
|
||||
"""Login to the lab vCenter and complete NFC authd for the temp VM."""
|
||||
return nfc_auth.authenticate(
|
||||
host=self.HOST,
|
||||
username=self.USERNAME,
|
||||
password=self.PASSWORD,
|
||||
vm_moref=self.VM_MOREF,
|
||||
thumbprint=self.THUMBPRINT,
|
||||
allow_untrusted=self.ALLOW_UNTRUSTED,
|
||||
disk_path=None if read_only else self.DISK_PATH,
|
||||
read_only=read_only)
|
||||
|
||||
def vixdisklib_connect_kwargs(
|
||||
self, extra: Optional[dict[str, Any]] = None) -> dict[str, Any]:
|
||||
"""Return common ``VixDiskLib_ConnectEx`` arguments for the temp VM."""
|
||||
kwargs: dict[str, Any] = {
|
||||
"server_name": self.HOST,
|
||||
"port": self.PORT,
|
||||
"thumbprint": self.THUMBPRINT,
|
||||
"username": self.USERNAME,
|
||||
"password": self.PASSWORD,
|
||||
"vmx_spec": self.VMX_SPEC,
|
||||
"transport_modes": "nbd",
|
||||
"read_only": False,
|
||||
}
|
||||
if extra:
|
||||
kwargs.update(extra)
|
||||
return kwargs
|
||||
|
||||
def pattern_bytes(self, length: int, seed: bytes) -> bytes:
|
||||
"""Return ``length`` bytes by repeating ``seed``."""
|
||||
if not seed:
|
||||
raise ValueError("seed must be non-empty")
|
||||
return (seed * ((length // len(seed)) + 1))[:length]
|
||||
@@ -0,0 +1,94 @@
|
||||
# Copyright 2026 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
|
||||
"""Compare writes and reads from VDDK with openvixdisklib."""
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
from openvixdisklib import openvixdisklib as open_vix
|
||||
from tests.integration import vixdisklib
|
||||
from tests.integration.base import TestBase
|
||||
|
||||
|
||||
class CrosscheckTest(TestBase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
"""Skip when the bundled VDDK shared library is not present."""
|
||||
cls.require_vddk()
|
||||
super().setUpClass()
|
||||
|
||||
def _connect_extra(self, module: Any) -> Optional[dict[str, Any]]:
|
||||
"""Return extra ``connect`` kwargs needed by ``module``."""
|
||||
if module is open_vix:
|
||||
return {"allow_untrusted": self.ALLOW_UNTRUSTED}
|
||||
return None
|
||||
|
||||
def _write_sectors(
|
||||
self,
|
||||
module: Any,
|
||||
payloads: dict[int, bytes]) -> None:
|
||||
"""Write one sector at each index using a vixdisklib-compatible module."""
|
||||
handle = module.VixDiskLibHandle(
|
||||
vixdisklib_compatibility_version="8.0",
|
||||
config_path=None)
|
||||
buf = module.get_buffer(self.SECTOR_SIZE)
|
||||
kwargs = self.vixdisklib_connect_kwargs(self._connect_extra(module))
|
||||
with handle.connect(**kwargs) as conn:
|
||||
with handle.open(conn, self.DISK_PATH, flags=0) as disk:
|
||||
for start, data in payloads.items():
|
||||
buf[:self.SECTOR_SIZE] = data
|
||||
handle.write(disk, start, 1, buf)
|
||||
|
||||
def _read_sectors(
|
||||
self,
|
||||
module: Any,
|
||||
sectors: tuple[int, ...]) -> dict[int, bytes]:
|
||||
"""Read one sector at each index using a vixdisklib-compatible module."""
|
||||
handle = module.VixDiskLibHandle(
|
||||
vixdisklib_compatibility_version="8.0",
|
||||
config_path=None)
|
||||
buf = module.get_buffer(self.SECTOR_SIZE)
|
||||
result: dict[int, bytes] = {}
|
||||
kwargs = self.vixdisklib_connect_kwargs(self._connect_extra(module))
|
||||
with handle.connect(**kwargs) as conn:
|
||||
with handle.open(conn, self.DISK_PATH, flags=0) as disk:
|
||||
for start in sectors:
|
||||
buf[:self.SECTOR_SIZE] = b"\xa5" * self.SECTOR_SIZE
|
||||
handle.read(disk, start, 1, buf)
|
||||
result[start] = buf.raw[:self.SECTOR_SIZE]
|
||||
return result
|
||||
|
||||
def _assert_both_read(
|
||||
self,
|
||||
sectors: tuple[int, ...],
|
||||
expected: dict[int, bytes]) -> None:
|
||||
vddk = self._read_sectors(vixdisklib, sectors)
|
||||
replacement = self._read_sectors(open_vix, sectors)
|
||||
for start in sectors:
|
||||
self.assertEqual(
|
||||
vddk[start], expected[start],
|
||||
f"VDDK mismatch at sector {start}")
|
||||
self.assertEqual(
|
||||
replacement[start], expected[start],
|
||||
f"openvixdisklib mismatch at sector {start}")
|
||||
|
||||
def test_openvixdisklib_matches_vddk_sectors(self) -> None:
|
||||
"""Writes from either library must be visible to both readers."""
|
||||
sectors = (0, 1, self.SECTOR_AT_1GB)
|
||||
vddk_payloads = {
|
||||
0: self.pattern_bytes(self.SECTOR_SIZE, b"XCHK-VDDK-S0"),
|
||||
1: self.pattern_bytes(self.SECTOR_SIZE, b"XCHK-VDDK-S1"),
|
||||
self.SECTOR_AT_1GB: self.pattern_bytes(
|
||||
self.SECTOR_SIZE, b"XCHK-VDDK-1G"),
|
||||
}
|
||||
self._write_sectors(vixdisklib, vddk_payloads)
|
||||
self._assert_both_read(sectors, vddk_payloads)
|
||||
|
||||
ovdl_payloads = {
|
||||
0: self.pattern_bytes(self.SECTOR_SIZE, b"XCHK-OVDL-S0"),
|
||||
1: self.pattern_bytes(self.SECTOR_SIZE, b"XCHK-OVDL-S1"),
|
||||
self.SECTOR_AT_1GB: self.pattern_bytes(
|
||||
self.SECTOR_SIZE, b"XCHK-OVDL-1G"),
|
||||
}
|
||||
self._write_sectors(open_vix, ovdl_payloads)
|
||||
self._assert_both_read(sectors, ovdl_payloads)
|
||||
@@ -0,0 +1,18 @@
|
||||
# Copyright 2026 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
|
||||
"""Exercise VDDK-compatible NFC authentication against the lab vCenter."""
|
||||
|
||||
from tests.integration.base import TestBase
|
||||
|
||||
|
||||
class NfcAuthTest(TestBase):
|
||||
def test_authd_handshake_completes(self) -> None:
|
||||
"""Complete VIM login and authd PROXY through ``200 Connect``."""
|
||||
with self.authenticate() as session:
|
||||
ticket = session.ticket
|
||||
self.assertTrue(ticket.host)
|
||||
self.assertTrue(ticket.port)
|
||||
self.assertTrue(ticket.sessionId)
|
||||
self.assertTrue(session.authd_sock.version())
|
||||
self.assertTrue(session.authd_sock.cipher())
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright 2026 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
|
||||
"""Exercise NFC disk open and a one-sector write/read against the lab."""
|
||||
|
||||
from openvixdisklib import nfc_open
|
||||
from tests.integration.base import TestBase
|
||||
|
||||
|
||||
class NfcOpenTest(TestBase):
|
||||
def test_open_disk_and_read_first_sector(self) -> None:
|
||||
"""Open the temp VMDK, write sector 0, and read it back."""
|
||||
expected = self.pattern_bytes(self.SECTOR_SIZE, b"NFC-OPEN-S0")
|
||||
with self.authenticate(read_only=False) as session:
|
||||
with nfc_open.open_disk(
|
||||
session, self.DISK_PATH, read_only=False) as disk:
|
||||
self.assertEqual(disk.path, self.DISK_PATH)
|
||||
self.assertGreater(disk.handle, 0)
|
||||
self.assertEqual(disk.sector_size, self.SECTOR_SIZE)
|
||||
disk.write(0, 1, expected)
|
||||
got = disk.read(0, 1)
|
||||
self.assertIsNot(got, expected)
|
||||
self.assertEqual(got, expected)
|
||||
@@ -0,0 +1,54 @@
|
||||
# Copyright 2026 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
|
||||
"""Exercise NFC sector writes and reads against the lab vCenter."""
|
||||
|
||||
from openvixdisklib import nfc_open
|
||||
from tests.integration.base import TestBase
|
||||
|
||||
|
||||
class NfcReadWriteTest(TestBase):
|
||||
def test_sector_writes_and_reads(self) -> None:
|
||||
"""Write known patterns and read them back at several ranges."""
|
||||
ranges = [
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(1, 1),
|
||||
(8, 8),
|
||||
(0, 128),
|
||||
(0, 129),
|
||||
(256, 64),
|
||||
]
|
||||
with self.authenticate(read_only=False) as session:
|
||||
with nfc_open.open_disk(
|
||||
session, self.DISK_PATH, read_only=False) as disk:
|
||||
for start, n_sectors in ranges:
|
||||
length = n_sectors * self.SECTOR_SIZE
|
||||
seed = f"NFC-R{start}:{n_sectors}-".encode()
|
||||
to_write = self.pattern_bytes(length, seed)
|
||||
disk.write(start, n_sectors, to_write)
|
||||
got = disk.read(start, n_sectors)
|
||||
self.assertIsNot(got, to_write)
|
||||
self.assertEqual(len(got), length)
|
||||
self.assertEqual(got, to_write)
|
||||
|
||||
two_seed = b"NFC-TWO-SECTOR"
|
||||
two_to_write = self.pattern_bytes(
|
||||
2 * self.SECTOR_SIZE, two_seed)
|
||||
disk.write(0, 2, two_to_write)
|
||||
two_got = disk.read(0, 2)
|
||||
self.assertIsNot(two_got, two_to_write)
|
||||
self.assertEqual(two_got, two_to_write)
|
||||
self.assertEqual(
|
||||
disk.read(1, 1), two_to_write[self.SECTOR_SIZE:])
|
||||
|
||||
big_seed = b"NFC-129-SECTOR-WRITE"
|
||||
big_to_write = self.pattern_bytes(
|
||||
129 * self.SECTOR_SIZE, big_seed)
|
||||
disk.write(0, 129, big_to_write)
|
||||
big_got = disk.read(0, 129)
|
||||
self.assertIsNot(big_got, big_to_write)
|
||||
self.assertEqual(big_got, big_to_write)
|
||||
self.assertEqual(
|
||||
big_got[self.SECTOR_SIZE:2 * self.SECTOR_SIZE],
|
||||
big_to_write[self.SECTOR_SIZE:2 * self.SECTOR_SIZE])
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright 2026 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
|
||||
"""Exercise the VDDK-compatible openvixdisklib handle against the lab."""
|
||||
|
||||
from openvixdisklib import openvixdisklib as vixdisklib
|
||||
from tests.integration.base import TestBase
|
||||
|
||||
|
||||
class OpenVixDiskLibTest(TestBase):
|
||||
def test_write_and_read_sector_zero_and_one_gib(self) -> None:
|
||||
"""Write then read sector 0 and the sector at a 1 GiB offset."""
|
||||
handle = vixdisklib.VixDiskLibHandle(
|
||||
vixdisklib_compatibility_version="8.0",
|
||||
config_path=None)
|
||||
write_buf = vixdisklib.get_buffer(self.SECTOR_SIZE)
|
||||
read_buf = vixdisklib.get_buffer(self.SECTOR_SIZE)
|
||||
connect_kwargs = self.vixdisklib_connect_kwargs({
|
||||
"allow_untrusted": self.ALLOW_UNTRUSTED,
|
||||
})
|
||||
patterns = {
|
||||
0: self.pattern_bytes(self.SECTOR_SIZE, b"OVDL-S0"),
|
||||
self.SECTOR_AT_1GB: self.pattern_bytes(
|
||||
self.SECTOR_SIZE, b"OVDL-1GB"),
|
||||
}
|
||||
with handle.connect(**connect_kwargs) as conn:
|
||||
with handle.open(conn, self.DISK_PATH, flags=0) as disk:
|
||||
for start, expected in patterns.items():
|
||||
write_buf[:self.SECTOR_SIZE] = expected
|
||||
handle.write(disk, start, 1, write_buf)
|
||||
read_buf[:self.SECTOR_SIZE] = b"\xa5" * self.SECTOR_SIZE
|
||||
handle.read(disk, start, 1, read_buf)
|
||||
self.assertEqual(read_buf.raw[:self.SECTOR_SIZE], expected)
|
||||
@@ -0,0 +1,31 @@
|
||||
# Copyright 2026 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
|
||||
"""Exercise native VDDK via tests.integration.vixdisklib against the lab."""
|
||||
|
||||
from tests.integration import vixdisklib
|
||||
from tests.integration.base import TestBase
|
||||
|
||||
|
||||
class VddkTest(TestBase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
"""Skip when the bundled VDDK shared library is not present."""
|
||||
cls.require_vddk()
|
||||
super().setUpClass()
|
||||
|
||||
def test_write_and_read_first_sector(self) -> None:
|
||||
"""Open the temp VMDK with VDDK, write sector 0, and read it back."""
|
||||
handle = vixdisklib.VixDiskLibHandle(
|
||||
vixdisklib_compatibility_version="8.0",
|
||||
config_path=None)
|
||||
write_buf = vixdisklib.get_buffer(self.SECTOR_SIZE)
|
||||
read_buf = vixdisklib.get_buffer(self.SECTOR_SIZE)
|
||||
expected = self.pattern_bytes(self.SECTOR_SIZE, b"VDDK-S0")
|
||||
write_buf[:self.SECTOR_SIZE] = expected
|
||||
with handle.connect(**self.vixdisklib_connect_kwargs()) as conn:
|
||||
with handle.open(conn, self.DISK_PATH, flags=0) as disk:
|
||||
handle.write(disk, 0, 1, write_buf)
|
||||
read_buf[:self.SECTOR_SIZE] = b"\xa5" * self.SECTOR_SIZE
|
||||
handle.read(disk, 0, 1, read_buf)
|
||||
self.assertEqual(read_buf.raw[:self.SECTOR_SIZE], expected)
|
||||
@@ -0,0 +1,538 @@
|
||||
# Copyright 2019 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
# Generated from vixDiskLib 6.7.0-8535999 from:
|
||||
# $VIX_ROOT/doc/errors/errors.html
|
||||
|
||||
VIX_OK = 0
|
||||
VIX_E_FAIL = 1
|
||||
VIX_E_OUT_OF_MEMORY = 2
|
||||
VIX_E_INVALID_ARG = 3
|
||||
VIX_E_FILE_NOT_FOUND = 4
|
||||
VIX_E_OBJECT_IS_BUSY = 5
|
||||
VIX_E_NOT_SUPPORTED = 6
|
||||
VIX_E_FILE_ERROR = 7
|
||||
VIX_E_DISK_FULL = 8
|
||||
VIX_E_INCORRECT_FILE_TYPE = 9
|
||||
VIX_E_CANCELLED = 10
|
||||
VIX_E_FILE_READ_ONLY = 11
|
||||
VIX_E_FILE_ALREADY_EXISTS = 12
|
||||
VIX_E_FILE_ACCESS_ERROR = 13
|
||||
VIX_E_REQUIRES_LARGE_FILES = 14
|
||||
VIX_E_FILE_ALREADY_LOCKED = 15
|
||||
VIX_E_VMDB = 16
|
||||
VIX_E_NOT_SUPPORTED_ON_REMOTE_OBJECT = 20
|
||||
VIX_E_FILE_TOO_BIG = 21
|
||||
VIX_E_FILE_NAME_INVALID = 22
|
||||
VIX_E_ALREADY_EXISTS = 23
|
||||
VIX_E_BUFFER_TOOSMALL = 24
|
||||
VIX_E_OBJECT_NOT_FOUND = 25
|
||||
VIX_E_HOST_NOT_CONNECTED = 26
|
||||
VIX_E_INVALID_UTF = 8
|
||||
VIX_E_OPERATION_ALREADY_IN_PROGRESS = 31
|
||||
VIX_E_UNFINISHED_JOB = 29
|
||||
VIX_E_NEED_KEY = 30
|
||||
VIX_E_LICENSE = 32
|
||||
VIX_E_VM_HOST_DISCONNECTED = 34
|
||||
VIX_E_AUTHENTICATION_FAIL = 35
|
||||
VIX_E_HOST_CONNECTION_LOST = 36
|
||||
VIX_E_DUPLICATE_NAME = 41
|
||||
VIX_E_ARGUMENT_TOO_BIG = 44
|
||||
VIX_E_INVALID_HANDLE = 1000
|
||||
VIX_E_NOT_SUPPORTED_ON_HANDLE_TYPE = 1001
|
||||
VIX_E_TOO_MANY_HANDLES = 1002
|
||||
VIX_E_NOT_FOUND = 2000
|
||||
VIX_E_TYPE_MISMATCH = 2001
|
||||
VIX_E_INVALID_XML = 2002
|
||||
VIX_E_TIMEOUT_WAITING_FOR_TOOLS = 3000
|
||||
VIX_E_UNRECOGNIZED_COMMAND = 3001
|
||||
VIX_E_OP_NOT_SUPPORTED_ON_GUEST = 3003
|
||||
VIX_E_PROGRAM_NOT_STARTED = 3004
|
||||
VIX_E_CANNOT_START_READ_ONLY_VM = 3005
|
||||
VIX_E_VM_NOT_RUNNING = 3006
|
||||
VIX_E_VM_IS_RUNNING = 3007
|
||||
VIX_E_CANNOT_CONNECT_TO_VM = 3008
|
||||
VIX_E_POWEROP_SCRIPTS_NOT_AVAILABLE = 3009
|
||||
VIX_E_NO_GUEST_OS_INSTALLED = 3010
|
||||
VIX_E_VM_INSUFFICIENT_HOST_MEMORY = 3011
|
||||
VIX_E_SUSPEND_ERROR = 3012
|
||||
VIX_E_VM_NOT_ENOUGH_CPUS = 3013
|
||||
VIX_E_HOST_USER_PERMISSIONS = 3014
|
||||
VIX_E_GUEST_USER_PERMISSIONS = 3015
|
||||
VIX_E_TOOLS_NOT_RUNNING = 3016
|
||||
VIX_E_GUEST_OPERATIONS_PROHIBITED = 3017
|
||||
VIX_E_ANON_GUEST_OPERATIONS_PROHIBITED = 3018
|
||||
VIX_E_ROOT_GUEST_OPERATIONS_PROHIBITED = 3019
|
||||
VIX_E_MISSING_ANON_GUEST_ACCOUNT = 3023
|
||||
VIX_E_CANNOT_AUTHENTICATE_WITH_GUEST = 3024
|
||||
VIX_E_UNRECOGNIZED_COMMAND_IN_GUEST = 3025
|
||||
VIX_E_CONSOLE_GUEST_OPERATIONS_PROHIBITED = 3026
|
||||
VIX_E_MUST_BE_CONSOLE_USER = 3027
|
||||
VIX_E_VMX_MSG_DIALOG_AND_NO_UI = 3028
|
||||
VIX_E_OPERATION_NOT_ALLOWED_FOR_LOGIN_TYPE = 3031
|
||||
VIX_E_LOGIN_TYPE_NOT_SUPPORTED = 3032
|
||||
VIX_E_EMPTY_PASSWORD_NOT_ALLOWED_IN_GUEST = 3033
|
||||
VIX_E_INTERACTIVE_SESSION_NOT_PRESENT = 3034
|
||||
VIX_E_INTERACTIVE_SESSION_USER_MISMATCH = 3035
|
||||
VIX_E_CANNOT_POWER_ON_VM = 3041
|
||||
VIX_E_NO_DISPLAY_SERVER = 3043
|
||||
VIX_E_TOO_MANY_LOGONS = 3046
|
||||
VIX_E_INVALID_AUTHENTICATION_SESSION = 3047
|
||||
VIX_E_VM_NOT_FOUND = 4000
|
||||
VIX_E_NOT_SUPPORTED_FOR_VM_VERSION = 4001
|
||||
VIX_E_CANNOT_READ_VM_CONFIG = 4002
|
||||
VIX_E_TEMPLATE_VM = 4003
|
||||
VIX_E_VM_ALREADY_LOADED = 4004
|
||||
VIX_E_VM_ALREADY_UP_TO_DATE = 4006
|
||||
VIX_E_VM_UNSUPPORTED_GUEST = 4011
|
||||
VIX_E_UNRECOGNIZED_PROPERTY = 6000
|
||||
VIX_E_INVALID_PROPERTY_VALUE = 6001
|
||||
VIX_E_READ_ONLY_PROPERTY = 6002
|
||||
VIX_E_MISSING_REQUIRED_PROPERTY = 6003
|
||||
VIX_E_INVALID_SERIALIZED_DATA = 6004
|
||||
VIX_E_PROPERTY_TYPE_MISMATCH = 6005
|
||||
VIX_E_BAD_VM_INDEX = 8000
|
||||
VIX_E_INVALID_MESSAGE_HEADER = 10000
|
||||
VIX_E_INVALID_MESSAGE_BODY = 10001
|
||||
VIX_E_SNAPSHOT_INVAL = 13000
|
||||
VIX_E_SNAPSHOT_DUMPER = 13001
|
||||
VIX_E_SNAPSHOT_DISKLIB = 13002
|
||||
VIX_E_SNAPSHOT_NOTFOUND = 13003
|
||||
VIX_E_SNAPSHOT_EXISTS = 13004
|
||||
VIX_E_SNAPSHOT_VERSION = 13005
|
||||
VIX_E_SNAPSHOT_NOPERM = 13006
|
||||
VIX_E_SNAPSHOT_CONFIG = 13007
|
||||
VIX_E_SNAPSHOT_NOCHANGE = 13008
|
||||
VIX_E_SNAPSHOT_CHECKPOINT = 13009
|
||||
VIX_E_SNAPSHOT_LOCKED = 13010
|
||||
VIX_E_SNAPSHOT_INCONSISTENT = 13011
|
||||
VIX_E_SNAPSHOT_NAMETOOLONG = 13012
|
||||
VIX_E_SNAPSHOT_VIXFILE = 13013
|
||||
VIX_E_SNAPSHOT_DISKLOCKED = 13014
|
||||
VIX_E_SNAPSHOT_DUPLICATEDDISK = 13015
|
||||
VIX_E_SNAPSHOT_INDEPENDENTDISK = 13016
|
||||
VIX_E_SNAPSHOT_NONUNIQUE_NAME = 13017
|
||||
VIX_E_SNAPSHOT_MEMORY_ON_INDEPENDENT_DISK = 13018
|
||||
VIX_E_SNAPSHOT_MAXSNAPSHOTS = 13019
|
||||
VIX_E_SNAPSHOT_MIN_FREE_SPACE = 13020
|
||||
VIX_E_SNAPSHOT_HIERARCHY_TOODEEP = 13021
|
||||
VIX_E_SNAPSHOT_NOT_REVERTABLE = 13024
|
||||
VIX_E_HOST_DISK_INVALID_VALUE = 14003
|
||||
VIX_E_HOST_DISK_SECTORSIZE = 14004
|
||||
VIX_E_HOST_FILE_ERROR_EOF = 14005
|
||||
VIX_E_HOST_NETBLKDEV_HANDSHAKE = 14006
|
||||
VIX_E_HOST_SOCKET_CREATION_ERROR = 14007
|
||||
VIX_E_HOST_SERVER_NOT_FOUND = 14008
|
||||
VIX_E_HOST_NETWORK_CONN_REFUSED = 14009
|
||||
VIX_E_HOST_TCP_SOCKET_ERROR = 14010
|
||||
VIX_E_HOST_TCP_CONN_LOST = 14011
|
||||
VIX_E_HOST_NBD_HASHFILE_VOLUME = 14012
|
||||
VIX_E_HOST_NBD_HASHFILE_INIT = 14013
|
||||
VIX_E_DISK_INVAL = 16000
|
||||
VIX_E_DISK_NOINIT = 16001
|
||||
VIX_E_DISK_NOIO = 16002
|
||||
VIX_E_DISK_PARTIALCHAIN = 16003
|
||||
VIX_E_DISK_NEEDSREPAIR = 16006
|
||||
VIX_E_DISK_OUTOFRANGE = 16007
|
||||
VIX_E_DISK_CID_MISMATCH = 16008
|
||||
VIX_E_DISK_CANTSHRINK = 16009
|
||||
VIX_E_DISK_PARTMISMATCH = 16010
|
||||
VIX_E_DISK_UNSUPPORTEDDISKVERSION = 16011
|
||||
VIX_E_DISK_OPENPARENT = 16012
|
||||
VIX_E_DISK_NOTSUPPORTED = 16013
|
||||
VIX_E_DISK_NEEDKEY = 16014
|
||||
VIX_E_DISK_NOKEYOVERRIDE = 16015
|
||||
VIX_E_DISK_NOTENCRYPTED = 16016
|
||||
VIX_E_DISK_NOKEY = 16017
|
||||
VIX_E_DISK_INVALIDPARTITIONTABLE = 16018
|
||||
VIX_E_DISK_NOTNORMAL = 16019
|
||||
VIX_E_DISK_NOTENCDESC = 16020
|
||||
VIX_E_DISK_NEEDVMFS = 16022
|
||||
VIX_E_DISK_RAWTOOBIG = 16024
|
||||
VIX_E_DISK_TOOMANYOPENFILES = 16027
|
||||
VIX_E_DISK_TOOMANYREDO = 16028
|
||||
VIX_E_DISK_RAWTOOSMALL = 16029
|
||||
VIX_E_DISK_INVALIDCHAIN = 16030
|
||||
VIX_E_DISK_KEY_NOTFOUND = 16052
|
||||
VIX_E_DISK_SUBSYSTEM_INIT_FAIL = 16053
|
||||
VIX_E_DISK_INVALID_CONNECTION = 16054
|
||||
VIX_E_DISK_ENCODING = 16061
|
||||
VIX_E_DISK_CANTREPAIR = 16062
|
||||
VIX_E_DISK_INVALIDDISK = 16063
|
||||
VIX_E_DISK_NOLICENSE = 16064
|
||||
VIX_E_DISK_NODEVICE = 16065
|
||||
VIX_E_DISK_UNSUPPORTEDDEVICE = 16066
|
||||
VIX_E_DISK_CAPACITY_MISMATCH = 16067
|
||||
VIX_E_DISK_PARENT_NOTALLOWED = 16068
|
||||
VIX_E_DISK_ATTACH_ROOTLINK = 16069
|
||||
VIX_E_CRYPTO_UNKNOWN_ALGORITHM = 17000
|
||||
VIX_E_CRYPTO_BAD_BUFFER_SIZE = 17001
|
||||
VIX_E_CRYPTO_INVALID_OPERATION = 17002
|
||||
VIX_E_CRYPTO_RANDOM_DEVICE = 17003
|
||||
VIX_E_CRYPTO_NEED_PASSWORD = 17004
|
||||
VIX_E_CRYPTO_BAD_PASSWORD = 17005
|
||||
VIX_E_CRYPTO_NOT_IN_DICTIONARY = 17006
|
||||
VIX_E_CRYPTO_NO_CRYPTO = 17007
|
||||
VIX_E_CRYPTO_ERROR = 17008
|
||||
VIX_E_CRYPTO_BAD_FORMAT = 17009
|
||||
VIX_E_CRYPTO_LOCKED = 17010
|
||||
VIX_E_CRYPTO_EMPTY = 17011
|
||||
VIX_E_CRYPTO_KEYSAFE_LOCATOR = 17012
|
||||
VIX_E_CANNOT_CONNECT_TO_HOST = 18000
|
||||
VIX_E_NOT_FOR_REMOTE_HOST = 18001
|
||||
VIX_E_INVALID_HOSTNAME_SPECIFICATION = 18002
|
||||
VIX_E_SCREEN_CAPTURE_ERROR = 19000
|
||||
VIX_E_SCREEN_CAPTURE_BAD_FORMAT = 19001
|
||||
VIX_E_SCREEN_CAPTURE_COMPRESSION_FAIL = 19002
|
||||
VIX_E_SCREEN_CAPTURE_LARGE_DATA = 19003
|
||||
VIX_E_GUEST_VOLUMES_NOT_FROZEN = 20000
|
||||
VIX_E_NOT_A_FILE = 20001
|
||||
VIX_E_NOT_A_DIRECTORY = 20002
|
||||
VIX_E_NO_SUCH_PROCESS = 20003
|
||||
VIX_E_FILE_NAME_TOO_LONG = 20004
|
||||
VIX_E_OPERATION_DISABLED = 20005
|
||||
VIX_E_TOOLS_INSTALL_NO_IMAGE = 21000
|
||||
VIX_E_TOOLS_INSTALL_IMAGE_INACCESIBLE = 21001
|
||||
VIX_E_TOOLS_INSTALL_NO_DEVICE = 21002
|
||||
VIX_E_TOOLS_INSTALL_DEVICE_NOT_CONNECTED = 21003
|
||||
VIX_E_TOOLS_INSTALL_CANCELLED = 21004
|
||||
VIX_E_TOOLS_INSTALL_INIT_FAILED = 21005
|
||||
VIX_E_TOOLS_INSTALL_AUTO_NOT_SUPPORTED = 21006
|
||||
VIX_E_TOOLS_INSTALL_GUEST_NOT_READY = 21007
|
||||
VIX_E_TOOLS_INSTALL_SIG_CHECK_FAILED = 21008
|
||||
VIX_E_TOOLS_INSTALL_ERROR = 21009
|
||||
VIX_E_TOOLS_INSTALL_ALREADY_UP_TO_DATE = 21010
|
||||
VIX_E_TOOLS_INSTALL_IN_PROGRESS = 21011
|
||||
VIX_E_TOOLS_INSTALL_IMAGE_COPY_FAILED = 21012
|
||||
VIX_E_WRAPPER_WORKSTATION_NOT_INSTALLED = 22001
|
||||
VIX_E_WRAPPER_VERSION_NOT_FOUND = 22002
|
||||
VIX_E_WRAPPER_SERVICEPROVIDER_NOT_FOUND = 22003
|
||||
VIX_E_WRAPPER_PLAYER_NOT_INSTALLED = 22004
|
||||
VIX_E_WRAPPER_RUNTIME_NOT_INSTALLED = 22005
|
||||
VIX_E_WRAPPER_MULTIPLE_SERVICEPROVIDERS = 22006
|
||||
VIX_E_MNTAPI_MOUNTPT_NOT_FOUND = 24000
|
||||
VIX_E_MNTAPI_MOUNTPT_IN_USE = 24001
|
||||
VIX_E_MNTAPI_DISK_NOT_FOUND = 24002
|
||||
VIX_E_MNTAPI_DISK_NOT_MOUNTED = 24003
|
||||
VIX_E_MNTAPI_DISK_IS_MOUNTED = 24004
|
||||
VIX_E_MNTAPI_DISK_NOT_SAFE = 24005
|
||||
VIX_E_MNTAPI_DISK_CANT_OPEN = 24006
|
||||
VIX_E_MNTAPI_CANT_READ_PARTS = 24007
|
||||
VIX_E_MNTAPI_UMOUNT_APP_NOT_FOUND = 24008
|
||||
VIX_E_MNTAPI_UMOUNT = 24009
|
||||
VIX_E_MNTAPI_NO_MOUNTABLE_PARTITONS = 24010
|
||||
VIX_E_MNTAPI_PARTITION_RANGE = 24011
|
||||
VIX_E_MNTAPI_PERM = 24012
|
||||
VIX_E_MNTAPI_DICT = 24013
|
||||
VIX_E_MNTAPI_DICT_LOCKED = 24014
|
||||
VIX_E_MNTAPI_OPEN_HANDLES = 24015
|
||||
VIX_E_MNTAPI_CANT_MAKE_VAR_DIR = 24016
|
||||
VIX_E_MNTAPI_NO_ROOT = 24017
|
||||
VIX_E_MNTAPI_LOOP_FAILED = 24018
|
||||
VIX_E_MNTAPI_DAEMON = 24019
|
||||
VIX_E_MNTAPI_INTERNAL = 24020
|
||||
VIX_E_MNTAPI_SYSTEM = 24021
|
||||
VIX_E_MNTAPI_NO_CONNECTION_DETAILS = 24022
|
||||
VIX_E_MNTAPI_INCOMPATIBLE_VERSION = 24300
|
||||
VIX_E_MNTAPI_OS_ERROR = 24301
|
||||
VIX_E_MNTAPI_DRIVE_LETTER_IN_USE = 24302
|
||||
VIX_E_MNTAPI_DRIVE_LETTER_ALREADY_ASSIGNED = 24303
|
||||
VIX_E_MNTAPI_VOLUME_NOT_MOUNTED = 24304
|
||||
VIX_E_MNTAPI_VOLUME_ALREADY_MOUNTED = 24305
|
||||
VIX_E_MNTAPI_FORMAT_FAILURE = 24306
|
||||
VIX_E_MNTAPI_NO_DRIVER = 24307
|
||||
VIX_E_MNTAPI_ALREADY_OPENED = 24308
|
||||
VIX_E_MNTAPI_ITEM_NOT_FOUND = 24309
|
||||
VIX_E_MNTAPI_UNSUPPROTED_BOOT_LOADER = 24310
|
||||
VIX_E_MNTAPI_UNSUPPROTED_OS = 24311
|
||||
VIX_E_MNTAPI_CODECONVERSION = 24312
|
||||
VIX_E_MNTAPI_REGWRITE_ERROR = 24313
|
||||
VIX_E_MNTAPI_UNSUPPORTED_FT_VOLUME = 24314
|
||||
VIX_E_MNTAPI_PARTITION_NOT_FOUND = 24315
|
||||
VIX_E_MNTAPI_PUTFILE_ERROR = 24316
|
||||
VIX_E_MNTAPI_GETFILE_ERROR = 24317
|
||||
VIX_E_MNTAPI_REG_NOT_OPENED = 24318
|
||||
VIX_E_MNTAPI_REGDELKEY_ERROR = 24319
|
||||
VIX_E_MNTAPI_CREATE_PARTITIONTABLE_ERROR = 24320
|
||||
VIX_E_MNTAPI_OPEN_FAILURE = 24321
|
||||
VIX_E_MNTAPI_VOLUME_NOT_WRITABLE = 24322
|
||||
VIX_E_ASYNC_MIXEDMODE_UNSUPPORTED = 26000
|
||||
VIX_E_NET_HTTP_UNSUPPORTED_PROTOCOL = 30001
|
||||
VIX_E_NET_HTTP_URL_MALFORMAT = 30003
|
||||
VIX_E_NET_HTTP_COULDNT_RESOLVE_PROXY = 30005
|
||||
VIX_E_NET_HTTP_COULDNT_RESOLVE_HOST = 30006
|
||||
VIX_E_NET_HTTP_COULDNT_CONNECT = 30007
|
||||
VIX_E_NET_HTTP_HTTP_RETURNED_ERROR = 30022
|
||||
VIX_E_NET_HTTP_OPERATION_TIMEDOUT = 30028
|
||||
VIX_E_NET_HTTP_SSL_CONNECT_ERROR = 30035
|
||||
VIX_E_NET_HTTP_TOO_MANY_REDIRECTS = 30047
|
||||
VIX_E_NET_HTTP_TRANSFER = 30200
|
||||
VIX_E_NET_HTTP_SSL_SECURITY = 30201
|
||||
VIX_E_NET_HTTP_GENERIC = 30202
|
||||
|
||||
VIX_ERROR_CODE_MAP = {
|
||||
VIX_OK: "The operation was successful.",
|
||||
VIX_E_FAIL: "Unknown error.",
|
||||
VIX_E_OUT_OF_MEMORY: "Memory allocation failed. Out of memory.",
|
||||
VIX_E_INVALID_ARG: "One of the parameters was invalid.",
|
||||
VIX_E_FILE_NOT_FOUND: "A file was not found.",
|
||||
VIX_E_OBJECT_IS_BUSY: "This function cannot be performed because the handle is executing another function.",
|
||||
VIX_E_NOT_SUPPORTED: "The operation is not supported.",
|
||||
VIX_E_FILE_ERROR: "A file access error occurred on the host or guest operating system.",
|
||||
VIX_E_DISK_FULL: "An error occurred while writing a file; the disk is full. Data has not been saved. Free some disk space and try again.",
|
||||
VIX_E_INCORRECT_FILE_TYPE: "An error occurred while accessing a file: wrong file type.",
|
||||
VIX_E_CANCELLED: "The operation was canceled.",
|
||||
VIX_E_FILE_READ_ONLY: "The file is write-protected.",
|
||||
VIX_E_FILE_ALREADY_EXISTS: "The file already exists.",
|
||||
VIX_E_FILE_ACCESS_ERROR: "You do not have access rights to this file.",
|
||||
VIX_E_REQUIRES_LARGE_FILES: "The file system does not support large files.",
|
||||
VIX_E_FILE_ALREADY_LOCKED: "The file is already in use.",
|
||||
VIX_E_VMDB: "The system returned an error. Communication with the virtual machine might have been interrupted.",
|
||||
VIX_E_NOT_SUPPORTED_ON_REMOTE_OBJECT: "The command is not supported on remote objects.",
|
||||
VIX_E_FILE_TOO_BIG: "The file is too large for the file system.",
|
||||
VIX_E_FILE_NAME_INVALID: "The file name is not valid.",
|
||||
VIX_E_ALREADY_EXISTS: "Already exists.",
|
||||
VIX_E_BUFFER_TOOSMALL: "Buffer is too small.",
|
||||
VIX_E_OBJECT_NOT_FOUND: "The request refers to an object that does not exist.",
|
||||
VIX_E_HOST_NOT_CONNECTED: "Unable to connect to the host.",
|
||||
VIX_E_INVALID_UTF: "The string parameter has incorrect encoding.",
|
||||
VIX_E_OPERATION_ALREADY_IN_PROGRESS: "The operation is already in progress.",
|
||||
VIX_E_UNFINISHED_JOB: "The job has not finished.",
|
||||
VIX_E_NEED_KEY: "A decryption key is required to perform the operation.",
|
||||
VIX_E_LICENSE: "This operation is not supported with the current license.",
|
||||
VIX_E_VM_HOST_DISCONNECTED: "Unable to communicate with the virtual machine's host because it is disconnected.",
|
||||
VIX_E_AUTHENTICATION_FAIL: "Authentication for encrypted virtual machine failed.",
|
||||
VIX_E_HOST_CONNECTION_LOST: "The connection to the host was lost.",
|
||||
VIX_E_DUPLICATE_NAME: "Another object is using this name.",
|
||||
VIX_E_ARGUMENT_TOO_BIG: "One of the specified arguments is too large.",
|
||||
VIX_E_INVALID_HANDLE: "The handle is not a valid VIX object.",
|
||||
VIX_E_NOT_SUPPORTED_ON_HANDLE_TYPE: "The operation is not supported on this type of handle.",
|
||||
VIX_E_TOO_MANY_HANDLES: "Too many handles are open.",
|
||||
VIX_E_NOT_FOUND: "Invalid file. A required section of the file is missing.",
|
||||
VIX_E_TYPE_MISMATCH: "Invalid file. An object has the wrong type.",
|
||||
VIX_E_INVALID_XML: "Invalid file. The contents might be corrupt.",
|
||||
VIX_E_TIMEOUT_WAITING_FOR_TOOLS: "A timeout error occurred while waiting for .",
|
||||
VIX_E_UNRECOGNIZED_COMMAND: "The command is not recognized by the virtual machine.",
|
||||
VIX_E_OP_NOT_SUPPORTED_ON_GUEST: "The requested operation is not supported on this guest operating system.",
|
||||
VIX_E_PROGRAM_NOT_STARTED: "A program could not run on the guest operating system.",
|
||||
VIX_E_CANNOT_START_READ_ONLY_VM: "Cannot power on a read-only virtual machine.",
|
||||
VIX_E_VM_NOT_RUNNING: "The virtual machine needs to be powered on.",
|
||||
VIX_E_VM_IS_RUNNING: "The virtual machine should not be powered on. It is already running.",
|
||||
VIX_E_CANNOT_CONNECT_TO_VM: "Cannot connect to the virtual machine.",
|
||||
VIX_E_POWEROP_SCRIPTS_NOT_AVAILABLE: "Cannot execute scripts.",
|
||||
VIX_E_NO_GUEST_OS_INSTALLED: "There is no operating system installed in the virtual machine.",
|
||||
VIX_E_VM_INSUFFICIENT_HOST_MEMORY: "Not enough physical memory is available to power on this virtual machine.",
|
||||
VIX_E_SUSPEND_ERROR: "An error occurred while suspending the virtual machine.",
|
||||
VIX_E_VM_NOT_ENOUGH_CPUS: "This virtual machine is configured to run with 2 CPUs, but the host has only 1 CPU. The virtual machine cannot be powered on.",
|
||||
VIX_E_HOST_USER_PERMISSIONS: "Insufficient permissions in the host operating system.",
|
||||
VIX_E_GUEST_USER_PERMISSIONS: "Authentication failure or insufficient permissions in guest operating system.",
|
||||
VIX_E_TOOLS_NOT_RUNNING: " are not running in the guest.",
|
||||
VIX_E_GUEST_OPERATIONS_PROHIBITED: "Guest operations are not allowed on this virtual machine.",
|
||||
VIX_E_ANON_GUEST_OPERATIONS_PROHIBITED: "Anonymous guest operations are not allowed on this virtual machine. You must call VixVM_LoginInGuest before performing guest operations.",
|
||||
VIX_E_ROOT_GUEST_OPERATIONS_PROHIBITED: "Guest operations are not allowed for the administrative user on this virtual machine.",
|
||||
VIX_E_MISSING_ANON_GUEST_ACCOUNT: "The virtual machine configuration must specify the guest account name to be used for anonymous guest operations.",
|
||||
VIX_E_CANNOT_AUTHENTICATE_WITH_GUEST: "The virtual machine cannot authenticate users with guest.",
|
||||
VIX_E_UNRECOGNIZED_COMMAND_IN_GUEST: "The command is not recognized by .",
|
||||
VIX_E_CONSOLE_GUEST_OPERATIONS_PROHIBITED: "Guest operations are not allowed for console users on this virtual machine.",
|
||||
VIX_E_MUST_BE_CONSOLE_USER: "Only the console user can run the command.",
|
||||
VIX_E_VMX_MSG_DIALOG_AND_NO_UI: "The virtual machine is blocked waiting for a user operation.",
|
||||
VIX_E_OPERATION_NOT_ALLOWED_FOR_LOGIN_TYPE: "The command is not allowed by this login type.",
|
||||
VIX_E_LOGIN_TYPE_NOT_SUPPORTED: "This login type is not supported.",
|
||||
VIX_E_EMPTY_PASSWORD_NOT_ALLOWED_IN_GUEST: "The guest OS does not support empty passwords.",
|
||||
VIX_E_INTERACTIVE_SESSION_NOT_PRESENT: "The specified guest user must be logged in interactively to perform this operation.",
|
||||
VIX_E_INTERACTIVE_SESSION_USER_MISMATCH: "The specified guest user does not match the user currently logged in interactively.",
|
||||
VIX_E_CANNOT_POWER_ON_VM: "The virtual machine could not start.",
|
||||
VIX_E_NO_DISPLAY_SERVER: "Cannot launch the UI because no display server is present in the current environment.",
|
||||
VIX_E_TOO_MANY_LOGONS: "The supported number of active authentication sessions has been exceeded.",
|
||||
VIX_E_INVALID_AUTHENTICATION_SESSION: "The authenticaton session provided does not exist.",
|
||||
VIX_E_VM_NOT_FOUND: "The virtual machine cannot be found.",
|
||||
VIX_E_NOT_SUPPORTED_FOR_VM_VERSION: "The operation is not supported for this virtual machine version.",
|
||||
VIX_E_CANNOT_READ_VM_CONFIG: "Cannot read the virtual machine configuration file.",
|
||||
VIX_E_TEMPLATE_VM: "Cannot perform this operation on a template virtual machine.",
|
||||
VIX_E_VM_ALREADY_LOADED: "The virtual machine has already been loaded.",
|
||||
VIX_E_VM_ALREADY_UP_TO_DATE: "The virtual machine is already up-to-date.",
|
||||
VIX_E_VM_UNSUPPORTED_GUEST: "The specified guest operating system is not supported on the host that is the target of the operation.",
|
||||
VIX_E_UNRECOGNIZED_PROPERTY: "Unrecognized handle property identifier.",
|
||||
VIX_E_INVALID_PROPERTY_VALUE: "Invalid property value.",
|
||||
VIX_E_READ_ONLY_PROPERTY: "Cannot change a read-only property.",
|
||||
VIX_E_MISSING_REQUIRED_PROPERTY: "This handle is missing a required property.",
|
||||
VIX_E_INVALID_SERIALIZED_DATA: "A serialized object is invalid and cannot be deserialized.",
|
||||
VIX_E_PROPERTY_TYPE_MISMATCH: "The data provided does not match the property type.",
|
||||
VIX_E_BAD_VM_INDEX: "The index parameter does not correspond to a result set.",
|
||||
VIX_E_INVALID_MESSAGE_HEADER: "A message header was corrupted or has the incorrect version.",
|
||||
VIX_E_INVALID_MESSAGE_BODY: "A message body was corrupted or is missing.",
|
||||
VIX_E_SNAPSHOT_INVAL: "A snapshot-related error has occurred.",
|
||||
VIX_E_SNAPSHOT_DUMPER: "Unable to open the snapshot file.",
|
||||
VIX_E_SNAPSHOT_DISKLIB: "Disk error.",
|
||||
VIX_E_SNAPSHOT_NOTFOUND: "The snapshot does not exist.",
|
||||
VIX_E_SNAPSHOT_EXISTS: "The snapshot already exists.",
|
||||
VIX_E_SNAPSHOT_VERSION: "Snapshots are not allowed on this virtual machine.",
|
||||
VIX_E_SNAPSHOT_NOPERM: "Insufficient permissions.",
|
||||
VIX_E_SNAPSHOT_CONFIG: "There is an error in the configuration file.",
|
||||
VIX_E_SNAPSHOT_NOCHANGE: "The state of the virtual machine has not changed since the last snapshot operation.",
|
||||
VIX_E_SNAPSHOT_CHECKPOINT: "Unable to save the snapshot file.",
|
||||
VIX_E_SNAPSHOT_LOCKED: "A snapshot operation is already in progress.",
|
||||
VIX_E_SNAPSHOT_INCONSISTENT: "The snapshot files are in an inconsistent state.",
|
||||
VIX_E_SNAPSHOT_NAMETOOLONG: "The filename is too long.",
|
||||
VIX_E_SNAPSHOT_VIXFILE: "Cannot snapshot all metadata files.",
|
||||
VIX_E_SNAPSHOT_DISKLOCKED: "One or more of the disks are busy.",
|
||||
VIX_E_SNAPSHOT_DUPLICATEDDISK: "The virtual disk is used multiple times.",
|
||||
VIX_E_SNAPSHOT_INDEPENDENTDISK: "Cannot take snapshots of powered on virtual machines with independent disks.",
|
||||
VIX_E_SNAPSHOT_NONUNIQUE_NAME: "The name does not uniquely identify one snapshot.",
|
||||
VIX_E_SNAPSHOT_MEMORY_ON_INDEPENDENT_DISK: "Failed to take a memory snapshot because the virtual machine is configured with independent disks.",
|
||||
VIX_E_SNAPSHOT_MAXSNAPSHOTS: "Exceeded the maximum number of permitted snapshots.",
|
||||
VIX_E_SNAPSHOT_MIN_FREE_SPACE: "Available free space is less than the configured minimum free space.",
|
||||
VIX_E_SNAPSHOT_HIERARCHY_TOODEEP: "Snapshot hierarchy is too deep.",
|
||||
VIX_E_SNAPSHOT_NOT_REVERTABLE: "Cannot revert. The snapshot is .",
|
||||
VIX_E_HOST_DISK_INVALID_VALUE: "The specified device is not a valid physical disk device.",
|
||||
VIX_E_HOST_DISK_SECTORSIZE: "The disk sector size check failed.",
|
||||
VIX_E_HOST_FILE_ERROR_EOF: "Read beyond the end of file.",
|
||||
VIX_E_HOST_NETBLKDEV_HANDSHAKE: "Error in protocol.",
|
||||
VIX_E_HOST_SOCKET_CREATION_ERROR: "Unable to create a socket.",
|
||||
VIX_E_HOST_SERVER_NOT_FOUND: "The specified server could not be contacted.",
|
||||
VIX_E_HOST_NETWORK_CONN_REFUSED: "The server refused connection.",
|
||||
VIX_E_HOST_TCP_SOCKET_ERROR: "There was an error in communication.",
|
||||
VIX_E_HOST_TCP_CONN_LOST: "The connection was lost.",
|
||||
VIX_E_HOST_NBD_HASHFILE_VOLUME: "NBD_ERR_HASHFILE_VOLUME.",
|
||||
VIX_E_HOST_NBD_HASHFILE_INIT: "NBD_ERR_HASHFILE_INIT.",
|
||||
VIX_E_DISK_INVAL: "One of the parameters supplied is invalid.",
|
||||
VIX_E_DISK_NOINIT: "The disk library has not been initialized.",
|
||||
VIX_E_DISK_NOIO: "The called function requires the virtual disk to be opened for I/O.",
|
||||
VIX_E_DISK_PARTIALCHAIN: "The called function cannot be performed on partial chains. Open the parent virtual disk.",
|
||||
VIX_E_DISK_NEEDSREPAIR: "The specified virtual disk needs repair.",
|
||||
VIX_E_DISK_OUTOFRANGE: "You have requested access to an area of the virtual disk that is out of bounds.",
|
||||
VIX_E_DISK_CID_MISMATCH: "The parent virtual disk has been modified since the child was created. Parent virutal disk's content ID does not match with the parent content ID in the child.",
|
||||
VIX_E_DISK_CANTSHRINK: "The specified virtual disk cannot be shrunk because it is not the parent disk.",
|
||||
VIX_E_DISK_PARTMISMATCH: "The partition table on the physical disk has changed since the disk was created. Remove the physical disk from the virtual machine, then add it again.",
|
||||
VIX_E_DISK_UNSUPPORTEDDISKVERSION: "The version of the virtual disk is newer than the version supported by this program.",
|
||||
VIX_E_DISK_OPENPARENT: "The parent of this virtual disk could not be opened.",
|
||||
VIX_E_DISK_NOTSUPPORTED: "The specified feature is not supported by this version.",
|
||||
VIX_E_DISK_NEEDKEY: "One or more required keys were not provided.",
|
||||
VIX_E_DISK_NOKEYOVERRIDE: "Will not create an unencrypted child of an encrypted disk without explicit request.",
|
||||
VIX_E_DISK_NOTENCRYPTED: "Not an encrypted disk.",
|
||||
VIX_E_DISK_NOKEY: "No keys were supplied for encrypting the disk.",
|
||||
VIX_E_DISK_INVALIDPARTITIONTABLE: "The partition table is invalid.",
|
||||
VIX_E_DISK_NOTNORMAL: "Only sparse extents with embedded descriptors can be encrypted.",
|
||||
VIX_E_DISK_NOTENCDESC: "Not an encrypted descriptor file.",
|
||||
VIX_E_DISK_NEEDVMFS: "The file system is not VMFS.",
|
||||
VIX_E_DISK_RAWTOOBIG: "The physical disk is too big.",
|
||||
VIX_E_DISK_TOOMANYOPENFILES: "The host's limit for open files has been exceeded.",
|
||||
VIX_E_DISK_TOOMANYREDO: "Too many levels of redo logs.",
|
||||
VIX_E_DISK_RAWTOOSMALL: "The physical disk is too small.",
|
||||
VIX_E_DISK_INVALIDCHAIN: "Invalid disk chain: cannot mix hosted and managed style disks in the same chain.",
|
||||
VIX_E_DISK_KEY_NOTFOUND: "The specified key is not found in the disk database.",
|
||||
VIX_E_DISK_SUBSYSTEM_INIT_FAIL: "One or more required subsystems failed to initialize.",
|
||||
VIX_E_DISK_INVALID_CONNECTION: "Invalid connection handle.",
|
||||
VIX_E_DISK_ENCODING: "Disk encoding error.",
|
||||
VIX_E_DISK_CANTREPAIR: "The disk is corrupted and unrepairable.",
|
||||
VIX_E_DISK_INVALIDDISK: "The specified file is not a virtual disk.",
|
||||
VIX_E_DISK_NOLICENSE: "The host is not licensed for this feature.",
|
||||
VIX_E_DISK_NODEVICE: "The device does not exist.",
|
||||
VIX_E_DISK_UNSUPPORTEDDEVICE: "The operation is not supported on this type of device.",
|
||||
VIX_E_DISK_CAPACITY_MISMATCH: "The parent virtual disk's capacity is not the same as child's capacity.",
|
||||
VIX_E_DISK_PARENT_NOTALLOWED: "Disk type cannot be allowed as parent.",
|
||||
VIX_E_DISK_ATTACH_ROOTLINK: "Both parent and child virtual disks are root links.",
|
||||
VIX_E_CRYPTO_UNKNOWN_ALGORITHM: "Security library error.",
|
||||
VIX_E_CRYPTO_BAD_BUFFER_SIZE: "Security library error.",
|
||||
VIX_E_CRYPTO_INVALID_OPERATION: "Security library error.",
|
||||
VIX_E_CRYPTO_RANDOM_DEVICE: "Security library error.",
|
||||
VIX_E_CRYPTO_NEED_PASSWORD: "A password is required for this operation.",
|
||||
VIX_E_CRYPTO_BAD_PASSWORD: "Incorrect password.",
|
||||
VIX_E_CRYPTO_NOT_IN_DICTIONARY: "Security library error.",
|
||||
VIX_E_CRYPTO_NO_CRYPTO: "Security library error.",
|
||||
VIX_E_CRYPTO_ERROR: "Security library error.",
|
||||
VIX_E_CRYPTO_BAD_FORMAT: "Security library error.",
|
||||
VIX_E_CRYPTO_LOCKED: "Security library error.",
|
||||
VIX_E_CRYPTO_EMPTY: "Security library error.",
|
||||
VIX_E_CRYPTO_KEYSAFE_LOCATOR: "Security library error.",
|
||||
VIX_E_CANNOT_CONNECT_TO_HOST: "Cannot connect to the host.",
|
||||
VIX_E_NOT_FOR_REMOTE_HOST: "Only a local host can support this feature.",
|
||||
VIX_E_INVALID_HOSTNAME_SPECIFICATION: "Malformed hostname parameter. For the given service provider, the hostname must be a URL in the form https://<hostname>:<port>/sdk.",
|
||||
VIX_E_SCREEN_CAPTURE_ERROR: "Could not capture screen.",
|
||||
VIX_E_SCREEN_CAPTURE_BAD_FORMAT: "Requested unsupported format.",
|
||||
VIX_E_SCREEN_CAPTURE_COMPRESSION_FAIL: "Could not compress the screen capture.",
|
||||
VIX_E_SCREEN_CAPTURE_LARGE_DATA: "The screen capture data is larger than the maximum size.",
|
||||
VIX_E_GUEST_VOLUMES_NOT_FROZEN: "The drives are not frozen.",
|
||||
VIX_E_NOT_A_FILE: "The object is not a file.",
|
||||
VIX_E_NOT_A_DIRECTORY: "The object is not a directory.",
|
||||
VIX_E_NO_SUCH_PROCESS: "No such process.",
|
||||
VIX_E_FILE_NAME_TOO_LONG: "File name too long.",
|
||||
VIX_E_OPERATION_DISABLED: "The operation has been disabled by the guest operating system.",
|
||||
VIX_E_TOOLS_INSTALL_NO_IMAGE: "No .",
|
||||
VIX_E_TOOLS_INSTALL_IMAGE_INACCESIBLE: "The .",
|
||||
VIX_E_TOOLS_INSTALL_NO_DEVICE: "The guest operating system does not have a device configured for the .",
|
||||
VIX_E_TOOLS_INSTALL_DEVICE_NOT_CONNECTED: "The guest operating system device used for installation of .",
|
||||
VIX_E_TOOLS_INSTALL_CANCELLED: "The .",
|
||||
VIX_E_TOOLS_INSTALL_INIT_FAILED: "The .",
|
||||
VIX_E_TOOLS_INSTALL_AUTO_NOT_SUPPORTED: "The .",
|
||||
VIX_E_TOOLS_INSTALL_GUEST_NOT_READY: " are not running in the guest OS. Automatic upgrade is not possible.",
|
||||
VIX_E_TOOLS_INSTALL_SIG_CHECK_FAILED: "The .",
|
||||
VIX_E_TOOLS_INSTALL_ERROR: "The .",
|
||||
VIX_E_TOOLS_INSTALL_ALREADY_UP_TO_DATE: " are already up to date.",
|
||||
VIX_E_TOOLS_INSTALL_IN_PROGRESS: "A .",
|
||||
VIX_E_TOOLS_INSTALL_IMAGE_COPY_FAILED: "Could not copy .",
|
||||
VIX_E_WRAPPER_WORKSTATION_NOT_INSTALLED: "Service type VIX_SERVICEPROVIDER_VMWARE_WORKSTATION was specified but not installed.",
|
||||
VIX_E_WRAPPER_VERSION_NOT_FOUND: "The specified version was not found.",
|
||||
VIX_E_WRAPPER_SERVICEPROVIDER_NOT_FOUND: "The specified service provider was not found.",
|
||||
VIX_E_WRAPPER_PLAYER_NOT_INSTALLED: "Service type VIX_SERVICEPROVIDER_VMWARE_PLAYER was specified but not installed.",
|
||||
VIX_E_WRAPPER_RUNTIME_NOT_INSTALLED: "Cannot find support libraries; VIX appears to have not been installed.",
|
||||
VIX_E_WRAPPER_MULTIPLE_SERVICEPROVIDERS: "Cannot connect with multiple service providers.",
|
||||
VIX_E_MNTAPI_MOUNTPT_NOT_FOUND: "Could not find the specified mountpoint.",
|
||||
VIX_E_MNTAPI_MOUNTPT_IN_USE: "The mountpoint is already in use.",
|
||||
VIX_E_MNTAPI_DISK_NOT_FOUND: "Could not find the specified virtual disk.",
|
||||
VIX_E_MNTAPI_DISK_NOT_MOUNTED: "The specified disk is not mounted.",
|
||||
VIX_E_MNTAPI_DISK_IS_MOUNTED: "The specified disk is already mounted.",
|
||||
VIX_E_MNTAPI_DISK_NOT_SAFE: "It is not safe to mount the virtual disk. It might be attached to a suspended or powered-on virtual machine, or it may be inside a snapshot chain.",
|
||||
VIX_E_MNTAPI_DISK_CANT_OPEN: "Cannot open the virtual disk.",
|
||||
VIX_E_MNTAPI_CANT_READ_PARTS: "Cannot read or parse the partition table on the virtual disk.",
|
||||
VIX_E_MNTAPI_UMOUNT_APP_NOT_FOUND: "Could not find the umount application in a standard system directory such as /bin, /usr/bin, or /sbin.",
|
||||
VIX_E_MNTAPI_UMOUNT: "The umount command failed.",
|
||||
VIX_E_MNTAPI_NO_MOUNTABLE_PARTITONS: "The virtual disk does not have any partitions that the host system knows how to mount.",
|
||||
VIX_E_MNTAPI_PARTITION_RANGE: "An invalid partition number was specified.",
|
||||
VIX_E_MNTAPI_PERM: "Insufficient permissions to perform this operation.",
|
||||
VIX_E_MNTAPI_DICT: "Error accessing metadata. You might not have sufficient permission to access this disk or the metadata may be corrupted.",
|
||||
VIX_E_MNTAPI_DICT_LOCKED: "The metadata for this disk is locked. Check for other running virtual disk mounter applications.",
|
||||
VIX_E_MNTAPI_OPEN_HANDLES: "Another process is performing an operation on this mounted virtual disk.",
|
||||
VIX_E_MNTAPI_CANT_MAKE_VAR_DIR: "Cannot create directory '/var/run/vmware/fuse'.",
|
||||
VIX_E_MNTAPI_NO_ROOT: "This application must be run setuid root.",
|
||||
VIX_E_MNTAPI_LOOP_FAILED: "A loop device operation failed.",
|
||||
VIX_E_MNTAPI_DAEMON: "The VMware fuse daemon failed to start.",
|
||||
VIX_E_MNTAPI_INTERNAL: "An internal error has occurred. Contact VMware support.",
|
||||
VIX_E_MNTAPI_SYSTEM: "A system call has failed.",
|
||||
VIX_E_MNTAPI_NO_CONNECTION_DETAILS: "Unable to get vixDiskLib connection details.",
|
||||
VIX_E_MNTAPI_INCOMPATIBLE_VERSION: "The product version number is lower than the expected version number.",
|
||||
VIX_E_MNTAPI_OS_ERROR: "There was an operating system error.",
|
||||
VIX_E_MNTAPI_DRIVE_LETTER_IN_USE: "The specified drive letter is already in use.",
|
||||
VIX_E_MNTAPI_DRIVE_LETTER_ALREADY_ASSIGNED: "The specified drive letter is already assigned.",
|
||||
VIX_E_MNTAPI_VOLUME_NOT_MOUNTED: "The specified volume is not mounted.",
|
||||
VIX_E_MNTAPI_VOLUME_ALREADY_MOUNTED: "The specified volume is already mounted.",
|
||||
VIX_E_MNTAPI_FORMAT_FAILURE: "Unable to format volume.",
|
||||
VIX_E_MNTAPI_NO_DRIVER: "Driver not found.",
|
||||
VIX_E_MNTAPI_ALREADY_OPENED: "A handle to the Volume or DiskSet is already open.",
|
||||
VIX_E_MNTAPI_ITEM_NOT_FOUND: "Invalid file. A required section of the file is missing.",
|
||||
VIX_E_MNTAPI_UNSUPPROTED_BOOT_LOADER: "Boot loader not supported.",
|
||||
VIX_E_MNTAPI_UNSUPPROTED_OS: "The current operating system is not supported.",
|
||||
VIX_E_MNTAPI_CODECONVERSION: "An error occurred while converting the string.",
|
||||
VIX_E_MNTAPI_REGWRITE_ERROR: "There was an error writing to the registry.",
|
||||
VIX_E_MNTAPI_UNSUPPORTED_FT_VOLUME: "Windows NT4 Fault Tolerant volume type is not supported.",
|
||||
VIX_E_MNTAPI_PARTITION_NOT_FOUND: "The specified partition was not found.",
|
||||
VIX_E_MNTAPI_PUTFILE_ERROR: "Putfile error.",
|
||||
VIX_E_MNTAPI_GETFILE_ERROR: "Getfile error.",
|
||||
VIX_E_MNTAPI_REG_NOT_OPENED: "Unable to open registry key.",
|
||||
VIX_E_MNTAPI_REGDELKEY_ERROR: "There was an error deleting the registry key.",
|
||||
VIX_E_MNTAPI_CREATE_PARTITIONTABLE_ERROR: "An error occurred while creating the partition table.",
|
||||
VIX_E_MNTAPI_OPEN_FAILURE: "Failed to open DiskSet.",
|
||||
VIX_E_MNTAPI_VOLUME_NOT_WRITABLE: "The volume is write-protected.",
|
||||
VIX_E_ASYNC_MIXEDMODE_UNSUPPORTED: "Synchronous and asynchronous I/O on the same disk handle is not allowed.",
|
||||
VIX_E_NET_HTTP_UNSUPPORTED_PROTOCOL: "The URL provided uses an unsupported protocol.",
|
||||
VIX_E_NET_HTTP_URL_MALFORMAT: "The URL was not properly formatted.",
|
||||
VIX_E_NET_HTTP_COULDNT_RESOLVE_PROXY: "Failed to resolve proxy.",
|
||||
VIX_E_NET_HTTP_COULDNT_RESOLVE_HOST: "Failed to resolve host.",
|
||||
VIX_E_NET_HTTP_COULDNT_CONNECT: "Failed to connect to host or proxy.",
|
||||
VIX_E_NET_HTTP_HTTP_RETURNED_ERROR: "Server returned HTTP error code >= 400.",
|
||||
VIX_E_NET_HTTP_OPERATION_TIMEDOUT: "Network operation timed out.",
|
||||
VIX_E_NET_HTTP_SSL_CONNECT_ERROR: "A problem occurred during the SSL/TLS handshake.",
|
||||
VIX_E_NET_HTTP_TOO_MANY_REDIRECTS: "Reached the maximum number of redirects.",
|
||||
VIX_E_NET_HTTP_TRANSFER: "Failure sending/receiving network data.",
|
||||
VIX_E_NET_HTTP_SSL_SECURITY: "An SSL error occurred.",
|
||||
VIX_E_NET_HTTP_GENERIC: "A generic HTTP error occurred."
|
||||
}
|
||||
Executable
+324
@@ -0,0 +1,324 @@
|
||||
# Copyright 2016 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
|
||||
"""Python bindings for the VDDK vixdisklib library. Superseded by the
|
||||
openvixdisklib, which avoids the proprietary VDDK SDK.
|
||||
|
||||
This module is used by the integration tests in order to cross-check the
|
||||
openvixdisklib library. ctypes layouts follow `.vddk/vixDiskLib.h`.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import ctypes
|
||||
import logging
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from tests.integration import vix_disklib_errors
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
VIXDISKLIB_VERSION_MAJOR = 8
|
||||
VIXDISKLIB_VERSION_MINOR = 0
|
||||
|
||||
VIXDISKLIB_SECTOR_SIZE = 512
|
||||
|
||||
VIXDISKLIB_CRED_UID = 1
|
||||
|
||||
VIXDISKLIB_FLAG_OPEN_UNBUFFERED = 1
|
||||
VIXDISKLIB_FLAG_OPEN_SINGLE_LINK = 2
|
||||
VIXDISKLIB_FLAG_OPEN_READ_ONLY = 4
|
||||
|
||||
# NBD compression flags
|
||||
VIXDISKLIB_FLAG_OPEN_COMPRESSION_ZLIB = 16
|
||||
VIXDISKLIB_FLAG_OPEN_COMPRESSION_FASTLZ = 32
|
||||
VIXDISKLIB_FLAG_OPEN_COMPRESSION_SKIPZ = 64
|
||||
|
||||
VIX_SUPPORTED_COMPATIBILITY_MODES = [
|
||||
"6.0", "6.5", "6.7", "7.0", "8.0"]
|
||||
|
||||
|
||||
class VixDiskLibUidPasswdCreds(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("userName", ctypes.c_char_p),
|
||||
("password", ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibSessionIdCreds(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cookie", ctypes.c_char_p),
|
||||
("userName", ctypes.c_char_p),
|
||||
("key", ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibCreds(ctypes.Union):
|
||||
_fields_ = [
|
||||
("uid", VixDiskLibUidPasswdCreds),
|
||||
("sessionId", VixDiskLibSessionIdCreds),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibConnectParams(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("vmxSpec", ctypes.c_char_p),
|
||||
("serverName", ctypes.c_char_p),
|
||||
("thumbPrint", ctypes.c_char_p),
|
||||
# Note: this is 32bit on Windows
|
||||
("privateUse", ctypes.c_longlong),
|
||||
("credType", ctypes.c_uint32),
|
||||
("creds", VixDiskLibCreds),
|
||||
("port", ctypes.c_uint32),
|
||||
("nfcHostPort", ctypes.c_uint32),
|
||||
("vimApiVer", ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibConnection(ctypes.Structure):
|
||||
_fields_ = []
|
||||
|
||||
|
||||
def get_buffer(size):
|
||||
return ctypes.create_string_buffer(size)
|
||||
|
||||
|
||||
class VixDiskLibHandle(object):
|
||||
""" Class which acts as a proxy for vixDiskLib-related operations:
|
||||
"""
|
||||
def __init__(
|
||||
self, config_path=None, vixdisklib_compatibility_version=None):
|
||||
self._vix_disklib = ctypes.cdll.LoadLibrary(
|
||||
self.get_vix_disklib_name())
|
||||
self._setup_vix_disklib()
|
||||
|
||||
if config_path:
|
||||
config_path = config_path.encode()
|
||||
|
||||
target_versions = VIX_SUPPORTED_COMPATIBILITY_MODES
|
||||
if vixdisklib_compatibility_version:
|
||||
target_versions = [vixdisklib_compatibility_version]
|
||||
LOG.debug("vixDiskLib versions targeted: %s", target_versions)
|
||||
|
||||
# NOTE: iterate through all versions and try to initialize using each:
|
||||
version_used = None
|
||||
for version in reversed(target_versions):
|
||||
major_ver = None
|
||||
minor_ver = None
|
||||
try:
|
||||
major_ver, minor_ver = version.split(".")
|
||||
major_ver = int(major_ver)
|
||||
minor_ver = int(minor_ver)
|
||||
except ValueError as ex:
|
||||
raise ValueError(
|
||||
"Unsupported vixDiskLib version format '%s'. vixDiskLib "
|
||||
"compatibility mode must be of the form "
|
||||
"'$major.$minor'" % version) from ex
|
||||
|
||||
try:
|
||||
self._check_err(self._vix_disklib.VixDiskLib_InitEx(
|
||||
major_ver, minor_ver, None, None, None, None, config_path))
|
||||
version_used = version
|
||||
break
|
||||
except Exception:
|
||||
LOG.debug(
|
||||
"Failed to initialize vixDiskLib using compatibility "
|
||||
"version '%s'. Trying next version. Error trace: %s",
|
||||
version, traceback.format_exc())
|
||||
|
||||
if not version_used:
|
||||
raise Exception(
|
||||
"Could not initialize vixDiskLib with any of the following "
|
||||
"versions: %s" % target_versions)
|
||||
|
||||
LOG.info(
|
||||
"Successfully initialized vixDiskLib with target version '%s'",
|
||||
version_used)
|
||||
|
||||
@classmethod
|
||||
def get_vix_disklib_name(cls):
|
||||
vixDiskLibName = None
|
||||
if os.name == 'nt':
|
||||
vixDiskLibName = 'vixDiskLib.dll'
|
||||
else:
|
||||
vixDiskLibName = 'libvixDiskLib.so'
|
||||
return vixDiskLibName
|
||||
|
||||
def _setup_vix_disklib(self):
|
||||
self._vix_disklib.VixDiskLib_InitEx.argtypes = [
|
||||
ctypes.c_uint32, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p,
|
||||
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
||||
self._vix_disklib.VixDiskLib_InitEx.restype = ctypes.c_uint64
|
||||
|
||||
self._vix_disklib.VixDiskLib_GetErrorText.argtypes = [
|
||||
ctypes.c_uint64, ctypes.c_char_p]
|
||||
self._vix_disklib.VixDiskLib_GetErrorText.restype = ctypes.c_void_p
|
||||
|
||||
self._vix_disklib.VixDiskLib_FreeErrorText.arg_types = [
|
||||
ctypes.c_char_p]
|
||||
self._vix_disklib.VixDiskLib_FreeErrorText.restype = None
|
||||
|
||||
self._vix_disklib.VixDiskLib_ListTransportModes.argtypes = []
|
||||
self._vix_disklib.VixDiskLib_ListTransportModes.restype = (
|
||||
ctypes.c_char_p)
|
||||
|
||||
self._vix_disklib.VixDiskLib_GetTransportMode.argtypes = [
|
||||
ctypes.c_void_p]
|
||||
self._vix_disklib.VixDiskLib_GetTransportMode.restype = (
|
||||
ctypes.c_char_p)
|
||||
|
||||
self._vix_disklib.VixDiskLib_ConnectEx.argtypes = [
|
||||
ctypes.POINTER(VixDiskLibConnectParams), ctypes.c_char,
|
||||
ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)]
|
||||
self._vix_disklib.VixDiskLib_ConnectEx.restype = ctypes.c_uint64
|
||||
|
||||
self._vix_disklib.VixDiskLib_Open.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32,
|
||||
ctypes.POINTER(ctypes.c_void_p)]
|
||||
self._vix_disklib.VixDiskLib_Open.restype = ctypes.c_uint64
|
||||
|
||||
self._vix_disklib.VixDiskLib_Read.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_char_p]
|
||||
self._vix_disklib.VixDiskLib_Read.restype = ctypes.c_uint64
|
||||
|
||||
self._vix_disklib.VixDiskLib_Write.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_char_p]
|
||||
self._vix_disklib.VixDiskLib_Write.restype = ctypes.c_uint64
|
||||
|
||||
self._vix_disklib.VixDiskLib_GetMetadataKeys.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint64,
|
||||
ctypes.POINTER(ctypes.c_uint64)]
|
||||
self._vix_disklib.VixDiskLib_GetMetadataKeys.restype = ctypes.c_uint64
|
||||
|
||||
self._vix_disklib.VixDiskLib_ReadMetadata.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint64,
|
||||
ctypes.POINTER(ctypes.c_uint64)]
|
||||
self._vix_disklib.VixDiskLib_ReadMetadata.restype = ctypes.c_uint64
|
||||
|
||||
self._vix_disklib.VixDiskLib_Close.argtypes = [ctypes.c_void_p]
|
||||
self._vix_disklib.VixDiskLib_Close.restype = ctypes.c_uint64
|
||||
|
||||
self._vix_disklib.VixDiskLib_Disconnect.argtypes = [ctypes.c_void_p]
|
||||
self._vix_disklib.VixDiskLib_Disconnect.restype = ctypes.c_uint64
|
||||
|
||||
self._vix_disklib.VixDiskLib_Exit.argtypes = []
|
||||
self._vix_disklib.VixDiskLib_Exit.restype = None
|
||||
|
||||
def _check_err(self, err, allowed_values=[vix_disklib_errors.VIX_OK]):
|
||||
if err not in allowed_values:
|
||||
err_msg = self._vix_disklib.VixDiskLib_GetErrorText(err, None)
|
||||
err_msg_copy = str(ctypes.cast(
|
||||
err_msg, ctypes.c_char_p).value.decode())
|
||||
self._vix_disklib.VixDiskLib_FreeErrorText(
|
||||
ctypes.cast(err_msg, ctypes.c_char_p))
|
||||
|
||||
msg = None
|
||||
if err == vix_disklib_errors.VIX_E_OUT_OF_MEMORY:
|
||||
msg = (
|
||||
"The ESXi host performing the CBT export ran out of RAM. "
|
||||
"The host performing the export is automatically chosen "
|
||||
"by vCenter, so enough RAM to run the export is required "
|
||||
"on all hosts. To force the export from the specific host "
|
||||
"the VM is on, create a Coriolis endpoint with the DNS "
|
||||
"name/IP address of that host.")
|
||||
if err == vix_disklib_errors.VIX_E_HOST_NETWORK_CONN_REFUSED:
|
||||
msg = (
|
||||
"The ESXi host performing the CBT export refused "
|
||||
"connection. The host is chosen automatically by vCenter, "
|
||||
"so please ensure that the Coriolis deployment "
|
||||
"can dial TCP/902 on all of the ESXi hosts of a vSphere, "
|
||||
"and that DNS name resolution and firewalls are setup to "
|
||||
"facilitate this. Alternatively, try connecting Coriolis "
|
||||
"directly to the specific ESXi host which is running the "
|
||||
"VM(s) to be migrated by creating a Coriolis endpoint "
|
||||
"using the DNS name/IP address of the host itself.")
|
||||
|
||||
if err == vix_disklib_errors.VIX_E_CANNOT_CONNECT_TO_HOST:
|
||||
msg = (
|
||||
"Coriolis lost connection to the ESXi host performing the "
|
||||
"CBT export. If the Coriolis Endpoint connects to a "
|
||||
"vSphere host, please try connecting Coriolis to the ESXi "
|
||||
"host directly. If problem persists, try re-enabling CBT "
|
||||
"on the VM, or moving it to another ESXi host.")
|
||||
|
||||
err_msg = err_msg_copy
|
||||
if msg:
|
||||
LOG.debug("Original vixDiskLib error message: %s", err_msg_copy)
|
||||
err_msg = msg
|
||||
|
||||
raise Exception(err_msg)
|
||||
|
||||
def get_transport_modes(self):
|
||||
transport_modes = self._vix_disklib.VixDiskLib_ListTransportModes()
|
||||
return transport_modes.decode().split(':')
|
||||
|
||||
def get_transport_mode(self, disk_handle):
|
||||
t_mode = self._vix_disklib.VixDiskLib_GetTransportMode(disk_handle)
|
||||
return t_mode.decode()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def connect(
|
||||
self, server_name, thumbprint, username, password,
|
||||
vmx_spec=None, snapshot_ref=None, read_only=True,
|
||||
transport_modes=None, port=443):
|
||||
LOG.debug("Connecting VixDiskLib: %s", server_name)
|
||||
|
||||
connectParams = VixDiskLibConnectParams()
|
||||
|
||||
connectParams.serverName = server_name.encode()
|
||||
if vmx_spec:
|
||||
connectParams.vmxSpec = vmx_spec.encode()
|
||||
if thumbprint:
|
||||
connectParams.thumbPrint = thumbprint.encode()
|
||||
|
||||
connectParams.credType = VIXDISKLIB_CRED_UID
|
||||
connectParams.creds.uid.userName = username.encode()
|
||||
connectParams.creds.uid.password = password.encode()
|
||||
connectParams.port = port
|
||||
|
||||
if transport_modes:
|
||||
transport_modes = transport_modes.encode()
|
||||
|
||||
if snapshot_ref:
|
||||
snapshot_ref = snapshot_ref.encode()
|
||||
|
||||
conn = ctypes.c_void_p()
|
||||
self._check_err(self._vix_disklib.VixDiskLib_ConnectEx(
|
||||
connectParams, read_only, snapshot_ref, transport_modes,
|
||||
ctypes.byref(conn)))
|
||||
try:
|
||||
yield conn
|
||||
finally:
|
||||
self.disconnect(conn)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def open(self, conn, disk_path, flags=VIXDISKLIB_FLAG_OPEN_READ_ONLY):
|
||||
LOG.debug("Openning VixDiskLib disk: %s", disk_path)
|
||||
|
||||
disk_handle = ctypes.c_void_p()
|
||||
self._check_err(self._vix_disklib.VixDiskLib_Open(
|
||||
conn, disk_path.encode(), flags, ctypes.byref(disk_handle)))
|
||||
try:
|
||||
yield disk_handle
|
||||
finally:
|
||||
self.close(disk_handle)
|
||||
|
||||
def read(self, disk_handle, start_sector, num_sectors, buf):
|
||||
self._check_err(self._vix_disklib.VixDiskLib_Read(
|
||||
disk_handle, start_sector, num_sectors, buf))
|
||||
|
||||
def write(self, disk_handle, start_sector, num_sectors, buf):
|
||||
"""Write ``num_sectors`` from ``buf`` starting at ``start_sector``."""
|
||||
self._check_err(self._vix_disklib.VixDiskLib_Write(
|
||||
disk_handle, start_sector, num_sectors, buf))
|
||||
|
||||
def close(self, disk_handle):
|
||||
LOG.debug("Closing VixDiskLib disk handle: %s", disk_handle)
|
||||
self._check_err(self._vix_disklib.VixDiskLib_Close(disk_handle))
|
||||
|
||||
def disconnect(self, conn):
|
||||
LOG.debug("Disconnecting VixDiskLib")
|
||||
self._check_err(self._vix_disklib.VixDiskLib_Disconnect(conn))
|
||||
|
||||
def exit(self):
|
||||
self._vix_disklib.VixDiskLib_Exit()
|
||||
Reference in New Issue
Block a user