dissect.hypervisor 3.21.dev2__tar.gz → 3.21.dev4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/PKG-INFO +1 -1
  2. dissect_hypervisor-3.21.dev4/dissect/hypervisor/descriptor/vbox.py +226 -0
  3. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/vmdk.py +4 -2
  4. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect.hypervisor.egg-info/PKG-INFO +1 -1
  5. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect.hypervisor.egg-info/SOURCES.txt +3 -1
  6. dissect_hypervisor-3.21.dev4/tests/_data/descriptor/vbox/GOAD-DC01.vbox +201 -0
  7. dissect_hypervisor-3.21.dev4/tests/_data/descriptor/vbox/encrypted.vbox +103 -0
  8. dissect_hypervisor-3.21.dev4/tests/_util.py +7 -0
  9. dissect_hypervisor-3.21.dev4/tests/descriptor/test_hyperv.py +64 -0
  10. dissect_hypervisor-3.21.dev4/tests/descriptor/test_vbox.py +69 -0
  11. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/descriptor/test_vmx.py +3 -4
  12. dissect_hypervisor-3.21.dev4/tests/disk/test_asif.py +19 -0
  13. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/disk/test_hdd.py +18 -0
  14. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/disk/test_qcow2.py +44 -31
  15. dissect_hypervisor-3.21.dev4/tests/disk/test_vhd.py +65 -0
  16. dissect_hypervisor-3.21.dev4/tests/disk/test_vhdx.py +110 -0
  17. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/disk/test_vmdk.py +55 -14
  18. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/util/test_envelope.py +12 -9
  19. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/util/test_vmtar.py +19 -18
  20. dissect_hypervisor-3.21.dev2/dissect/hypervisor/descriptor/vbox.py +0 -22
  21. dissect_hypervisor-3.21.dev2/tests/conftest.py +0 -133
  22. dissect_hypervisor-3.21.dev2/tests/descriptor/test_hyperv.py +0 -63
  23. dissect_hypervisor-3.21.dev2/tests/descriptor/test_vbox.py +0 -43
  24. dissect_hypervisor-3.21.dev2/tests/disk/test_asif.py +0 -17
  25. dissect_hypervisor-3.21.dev2/tests/disk/test_vhd.py +0 -62
  26. dissect_hypervisor-3.21.dev2/tests/disk/test_vhdx.py +0 -104
  27. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/.git-blame-ignore-revs +0 -0
  28. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/COPYRIGHT +0 -0
  29. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/LICENSE +0 -0
  30. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/MANIFEST.in +0 -0
  31. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/README.md +0 -0
  32. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/__init__.py +0 -0
  33. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/descriptor/__init__.py +0 -0
  34. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/descriptor/c_hyperv.py +0 -0
  35. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/descriptor/hyperv.py +0 -0
  36. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/descriptor/ovf.py +0 -0
  37. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/descriptor/pvs.py +0 -0
  38. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/descriptor/vmx.py +0 -0
  39. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/__init__.py +0 -0
  40. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/asif.py +0 -0
  41. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/c_asif.py +0 -0
  42. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/c_asif.pyi +0 -0
  43. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/c_hdd.py +0 -0
  44. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/c_qcow2.py +0 -0
  45. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/c_qcow2.pyi +0 -0
  46. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/c_vdi.py +0 -0
  47. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/c_vhd.py +0 -0
  48. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/c_vhdx.py +0 -0
  49. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/c_vmdk.py +0 -0
  50. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/hdd.py +0 -0
  51. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/qcow2.py +0 -0
  52. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/vdi.py +0 -0
  53. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/vhd.py +0 -0
  54. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/disk/vhdx.py +0 -0
  55. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/exceptions.py +0 -0
  56. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/tools/__init__.py +0 -0
  57. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/tools/envelope.py +0 -0
  58. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/tools/vmtar.py +0 -0
  59. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/util/__init__.py +0 -0
  60. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/util/envelope.py +0 -0
  61. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect/hypervisor/util/vmtar.py +0 -0
  62. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect.hypervisor.egg-info/dependency_links.txt +0 -0
  63. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect.hypervisor.egg-info/entry_points.txt +0 -0
  64. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect.hypervisor.egg-info/requires.txt +0 -0
  65. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/dissect.hypervisor.egg-info/top_level.txt +0 -0
  66. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/pyproject.toml +0 -0
  67. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/setup.cfg +0 -0
  68. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/__init__.py +0 -0
  69. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/descriptor/hyperv/test.VMRS +0 -0
  70. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/descriptor/hyperv/test.vmcx +0 -0
  71. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/descriptor/vmx/encrypted.vmx +0 -0
  72. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/asif/basic.asif.gz +0 -0
  73. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/expanding.hdd/DiskDescriptor.xml +0 -0
  74. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/expanding.hdd/expanding.hdd +0 -0
  75. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/expanding.hdd/expanding.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  76. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/plain.hdd/DiskDescriptor.xml +0 -0
  77. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/plain.hdd/plain.hdd +0 -0
  78. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/plain.hdd/plain.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  79. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/split.hdd/DiskDescriptor.xml +0 -0
  80. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/split.hdd/split.hdd +0 -0
  81. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/split.hdd/split.hdd.0.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  82. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/split.hdd/split.hdd.1.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  83. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/split.hdd/split.hdd.2.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  84. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/split.hdd/split.hdd.3.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  85. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/split.hdd/split.hdd.4.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  86. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/hdd/split.hdd/split.hdd.5.{5fbaabe3-6958-40ff-92a7-860e329aab41}.hds.gz +0 -0
  87. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/qcow2/backing-chain-1.qcow2.gz +0 -0
  88. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/qcow2/backing-chain-2.qcow2.gz +0 -0
  89. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/qcow2/backing-chain-3.qcow2.gz +0 -0
  90. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/qcow2/basic-zstd.qcow2.gz +0 -0
  91. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/qcow2/basic.qcow2.gz +0 -0
  92. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/qcow2/data-file.bin.gz +0 -0
  93. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/qcow2/data-file.qcow2.gz +0 -0
  94. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/qcow2/snapshot.qcow2.gz +0 -0
  95. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/vhd/dynamic.vhd.gz +0 -0
  96. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/vhd/fixed.vhd.gz +0 -0
  97. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/vhdx/differencing.avhdx.gz +0 -0
  98. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/vhdx/dynamic.vhdx.gz +0 -0
  99. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/vhdx/fixed.vhdx.gz +0 -0
  100. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/disk/vmdk/sesparse.vmdk.gz +0 -0
  101. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/util/envelope/encryption.info +0 -0
  102. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/util/envelope/local.tgz.ve +0 -0
  103. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_data/util/vmtar/test.vgz +0 -0
  104. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_docs/Makefile +0 -0
  105. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_docs/conf.py +0 -0
  106. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/_docs/index.rst +0 -0
  107. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/descriptor/__init__.py +0 -0
  108. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/descriptor/test_ovf.py +0 -0
  109. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/descriptor/test_pvs.py +0 -0
  110. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/disk/__init__.py +0 -0
  111. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tests/util/__init__.py +0 -0
  112. {dissect_hypervisor-3.21.dev2 → dissect_hypervisor-3.21.dev4}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.hypervisor
3
- Version: 3.21.dev2
3
+ Version: 3.21.dev4
4
4
  Summary: A Dissect module implementing parsers for various hypervisor disk, backup and configuration files
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -0,0 +1,226 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import datetime
4
+ from functools import cached_property
5
+ from typing import TYPE_CHECKING, TextIO
6
+ from uuid import UUID
7
+
8
+ from defusedxml import ElementTree
9
+
10
+ if TYPE_CHECKING:
11
+ from xml.etree.ElementTree import Element
12
+
13
+ NS = "{http://www.virtualbox.org/}"
14
+
15
+
16
+ class VBox:
17
+ """VirtualBox XML descriptor parser.
18
+
19
+ Args:
20
+ fh: A file-like object of the VirtualBox XML descriptor.
21
+ """
22
+
23
+ def __init__(self, fh: TextIO):
24
+ self._xml: Element = ElementTree.fromstring(fh.read())
25
+ if self._xml.tag != f"{NS}VirtualBox":
26
+ raise ValueError("Invalid VirtualBox XML descriptor: root element is not VirtualBox")
27
+
28
+ if (machine := self._xml.find(f"./{NS}Machine")) is None:
29
+ raise ValueError("Invalid VirtualBox XML descriptor: no Machine element found")
30
+
31
+ if machine.find(f"./{NS}Hardware") is None:
32
+ raise ValueError("Invalid VirtualBox XML descriptor: no Hardware element found")
33
+
34
+ self.machine = Machine(self, machine)
35
+
36
+ def __repr__(self) -> str:
37
+ return f"<VBox uuid={self.uuid} name={self.name}>"
38
+
39
+ @property
40
+ def uuid(self) -> UUID | None:
41
+ """The VM UUID."""
42
+ return self.machine.uuid
43
+
44
+ @property
45
+ def name(self) -> str | None:
46
+ """The VM name."""
47
+ return self.machine.name
48
+
49
+ @property
50
+ def media(self) -> dict[UUID, HardDisk]:
51
+ """The media (disks) registry."""
52
+ return self.machine.media
53
+
54
+ @property
55
+ def hardware(self) -> Hardware:
56
+ """The current machine hardware state."""
57
+ return self.machine.hardware
58
+
59
+ @property
60
+ def snapshots(self) -> dict[UUID, Snapshot]:
61
+ """All snapshots."""
62
+ return self.machine.snapshots
63
+
64
+
65
+ class Machine:
66
+ def __init__(self, vbox: VBox, element: Element):
67
+ self.vbox = vbox
68
+ self.element = element
69
+
70
+ def __repr__(self) -> str:
71
+ return f"<Machine uuid={self.uuid} name={self.name}>"
72
+
73
+ @property
74
+ def uuid(self) -> UUID:
75
+ """The machine UUID."""
76
+ return UUID(self.element.get("uuid").strip("{}"))
77
+
78
+ @property
79
+ def name(self) -> str:
80
+ """The machine name."""
81
+ return self.element.get("name")
82
+
83
+ @property
84
+ def current_snapshot(self) -> UUID | None:
85
+ """The current snapshot UUID."""
86
+ if (value := self.element.get("currentSnapshot")) is not None:
87
+ return UUID(value.strip("{}"))
88
+ return None
89
+
90
+ @cached_property
91
+ def media(self) -> dict[UUID, HardDisk]:
92
+ """The media (disks) registry."""
93
+ result = {}
94
+
95
+ stack = [(None, element) for element in self.element.find(f"./{NS}MediaRegistry/{NS}HardDisks")]
96
+ while stack:
97
+ parent, element = stack.pop()
98
+ hdd = HardDisk(self, element, parent)
99
+ result[hdd.uuid] = hdd
100
+
101
+ stack.extend([(hdd, child) for child in element.findall(f"./{NS}HardDisk")])
102
+
103
+ return result
104
+
105
+ @cached_property
106
+ def hardware(self) -> Hardware:
107
+ """The machine hardware state."""
108
+ return Hardware(self.vbox, self.element.find(f"./{NS}Hardware"))
109
+
110
+ @cached_property
111
+ def snapshots(self) -> dict[UUID, Snapshot]:
112
+ """All snapshots."""
113
+ result = {}
114
+
115
+ if (element := self.element.find(f"./{NS}Snapshot")) is None:
116
+ return result
117
+
118
+ stack = [(None, element)]
119
+ while stack:
120
+ parent, element = stack.pop()
121
+ snapshot = Snapshot(self.vbox, element, parent)
122
+ result[snapshot.uuid] = snapshot
123
+
124
+ if (snapshots := element.find(f"./{NS}Snapshots")) is not None:
125
+ stack.extend([(snapshot, child) for child in list(snapshots)])
126
+
127
+ return result
128
+
129
+ @property
130
+ def parent(self) -> Snapshot | None:
131
+ if (uuid := self.current_snapshot) is not None:
132
+ return self.vbox.snapshots[uuid]
133
+ return None
134
+
135
+
136
+ class HardDisk:
137
+ def __init__(self, vbox: VBox, element: Element, parent: HardDisk | None = None):
138
+ self.vbox = vbox
139
+ self.element = element
140
+ self.parent = parent
141
+
142
+ def __repr__(self) -> str:
143
+ return f"<HardDisk uuid={self.uuid} location={self.location}>"
144
+
145
+ @property
146
+ def uuid(self) -> UUID:
147
+ """The disk UUID."""
148
+ return UUID(self.element.get("uuid").strip("{}"))
149
+
150
+ @property
151
+ def location(self) -> str:
152
+ """The disk location."""
153
+ return self.element.get("location")
154
+
155
+ @property
156
+ def type(self) -> str | None:
157
+ """The disk type."""
158
+ return self.element.get("type")
159
+
160
+ @property
161
+ def format(self) -> str:
162
+ """The disk format."""
163
+ return self.element.get("format")
164
+
165
+ @cached_property
166
+ def properties(self) -> dict[str, str]:
167
+ """The disk properties."""
168
+ return {prop.get("name"): prop.get("value") for prop in self.element.findall(f"./{NS}Property")}
169
+
170
+ @property
171
+ def is_encrypted(self) -> bool:
172
+ """Whether the disk is encrypted."""
173
+ disk = self
174
+ while disk is not None:
175
+ if "CRYPT/KeyId" in disk.properties or "CRYPT/KeyStore" in disk.properties:
176
+ return True
177
+ disk = disk.parent
178
+
179
+ return False
180
+
181
+
182
+ class Snapshot:
183
+ def __init__(self, vbox: VBox, element: Element, parent: Snapshot | Machine | None = None):
184
+ self.vbox = vbox
185
+ self.element = element
186
+ self.parent = parent
187
+
188
+ def __repr__(self) -> str:
189
+ return f"<Snapshot uuid={self.uuid} name={self.name}>"
190
+
191
+ @property
192
+ def uuid(self) -> UUID:
193
+ """The snapshot UUID."""
194
+ return UUID(self.element.get("uuid").strip("{}"))
195
+
196
+ @property
197
+ def name(self) -> str:
198
+ """The snapshot name."""
199
+ return self.element.get("name")
200
+
201
+ @property
202
+ def ts(self) -> datetime:
203
+ """The snapshot timestamp."""
204
+ return datetime.strptime(self.element.get("timeStamp"), "%Y-%m-%dT%H:%M:%S%z")
205
+
206
+ @cached_property
207
+ def hardware(self) -> Hardware:
208
+ """The snapshot hardware state."""
209
+ return Hardware(self.vbox, self.element.find(f"./{NS}Hardware"))
210
+
211
+
212
+ class Hardware:
213
+ def __init__(self, vbox: VBox, element: Element):
214
+ self.vbox = vbox
215
+ self.element = element
216
+
217
+ def __repr__(self) -> str:
218
+ return f"<Hardware disks={len(self.disks)}>"
219
+
220
+ @property
221
+ def disks(self) -> list[HardDisk]:
222
+ """All attached hard disks."""
223
+ images = self.element.findall(
224
+ f"./{NS}StorageControllers/{NS}StorageController/{NS}AttachedDevice[@type='HardDisk']/{NS}Image"
225
+ )
226
+ return [self.vbox.media[UUID(image.get("uuid").strip("{}"))] for image in images]
@@ -535,8 +535,10 @@ def open_parent(path: Path, filename_hint: str) -> VMDK:
535
535
  hint_path, _, filename = filename_hint.rpartition("/")
536
536
  filepath = path.joinpath(filename)
537
537
  if not filepath.exists():
538
- _, _, hint_path_name = hint_path.rpartition("/")
539
- filepath = path.parent.joinpath(hint_path_name).joinpath(filename)
538
+ filepath = path.joinpath(filename_hint)
539
+ if not filepath.exists():
540
+ _, _, hint_path_name = hint_path.rpartition("/")
541
+ filepath = path.parent.joinpath(hint_path_name).joinpath(filename)
540
542
  vmdk = VMDK(filepath)
541
543
  except Exception as err:
542
544
  raise IOError(f"Failed to open parent disk with hint {filename_hint} from path {path}: {err}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.hypervisor
3
- Version: 3.21.dev2
3
+ Version: 3.21.dev4
4
4
  Summary: A Dissect module implementing parsers for various hypervisor disk, backup and configuration files
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -44,9 +44,11 @@ dissect/hypervisor/util/__init__.py
44
44
  dissect/hypervisor/util/envelope.py
45
45
  dissect/hypervisor/util/vmtar.py
46
46
  tests/__init__.py
47
- tests/conftest.py
47
+ tests/_util.py
48
48
  tests/_data/descriptor/hyperv/test.VMRS
49
49
  tests/_data/descriptor/hyperv/test.vmcx
50
+ tests/_data/descriptor/vbox/GOAD-DC01.vbox
51
+ tests/_data/descriptor/vbox/encrypted.vbox
50
52
  tests/_data/descriptor/vmx/encrypted.vmx
51
53
  tests/_data/disk/asif/basic.asif.gz
52
54
  tests/_data/disk/hdd/expanding.hdd/DiskDescriptor.xml
@@ -0,0 +1,201 @@
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ ** DO NOT EDIT THIS FILE.
4
+ ** If you make changes to this file while any VirtualBox related application
5
+ ** is running, your changes will be overwritten later, without taking effect.
6
+ ** Use VBoxManage or the VirtualBox Manager GUI to make changes.
7
+ **
8
+ ** Written by VirtualBox 7.2.4_RPMFUSION (r170995)
9
+ -->
10
+ <VirtualBox xmlns="http://www.virtualbox.org/" version="1.19-linux">
11
+ <Machine uuid="{a6277950-3d1b-45d3-b2fd-dc1f385027e1}" name="GOAD-DC01" OSType="Windows2016_64" currentSnapshot="{264ccd7e-9ffd-45ba-bd0e-4e1968d3355a}" snapshotFolder="Snapshots" lastStateChange="2025-12-19T21:00:43Z">
12
+ <MediaRegistry>
13
+ <HardDisks>
14
+ <HardDisk uuid="{0898f36f-01c3-4b43-8aeb-36ba7adaef95}" location="WindowsServer2019-disk001.vmdk" format="VMDK" type="Normal">
15
+ <HardDisk uuid="{3c72ec80-dc73-4448-a63e-97970cdd87e5}" location="Snapshots/{3c72ec80-dc73-4448-a63e-97970cdd87e5}.vmdk" format="VMDK">
16
+ <HardDisk uuid="{e6800503-8273-4f16-b584-c7ba2c1df698}" location="Snapshots/{e6800503-8273-4f16-b584-c7ba2c1df698}.vmdk" format="VMDK"/>
17
+ </HardDisk>
18
+ <!--
19
+ ** I ignored the big warning above and edited this file anyway. Yes, I'm kind of a rebel.
20
+ ** Don't tell VirtualBox about it, they get upset.
21
+ ** Manually add a "tree" of snapshots for testing purposes.
22
+ -->
23
+ <HardDisk uuid="{35bf5129-1caa-4117-b20a-d73868d9d5d2}" location="Snapshots/{35bf5129-1caa-4117-b20a-d73868d9d5d2}.vmdk" format="VMDK">
24
+ <HardDisk uuid="{706a96fe-0e11-4985-af32-7561d26612d4}" location="Snapshots/{706a96fe-0e11-4985-af32-7561d26612d4}.vmdk" format="VMDK"/>
25
+ </HardDisk>
26
+ </HardDisk>
27
+ </HardDisks>
28
+ </MediaRegistry>
29
+ <Snapshot uuid="{95b1572d-c893-48f0-9bc9-6a01a0fd2cb6}" name="push_1766163705_7292" timeStamp="2025-12-19T17:01:46Z">
30
+ <Hardware>
31
+ <Memory RAMSize="3000"/>
32
+ <Boot>
33
+ <Order position="1" device="HardDisk"/>
34
+ <Order position="2" device="DVD"/>
35
+ <Order position="3" device="None"/>
36
+ <Order position="4" device="None"/>
37
+ </Boot>
38
+ <Display VRAMSize="128"/>
39
+ <RemoteDisplay enabled="true">
40
+ <VRDEProperties>
41
+ <Property name="TCP/Address" value="127.0.0.1"/>
42
+ <Property name="TCP/Ports" value="5902"/>
43
+ </VRDEProperties>
44
+ </RemoteDisplay>
45
+ <Firmware/>
46
+ <BIOS>
47
+ <IOAPIC enabled="true"/>
48
+ <SmbiosUuidLittleEndian enabled="true"/>
49
+ </BIOS>
50
+ <Network>
51
+ <Adapter slot="0" enabled="true" MACAddress="0800277AA2FC" type="82540EM">
52
+ <NAT localhost-reachable="true">
53
+ <DNS use-proxy="true"/>
54
+ <Forwarding name="ssh" proto="1" hostip="127.0.0.1" hostport="2222" guestport="22"/>
55
+ <Forwarding name="winrm" proto="1" hostip="127.0.0.1" hostport="55985" guestport="5985"/>
56
+ <Forwarding name="winrm-ssl" proto="1" hostip="127.0.0.1" hostport="55986" guestport="5986"/>
57
+ </NAT>
58
+ </Adapter>
59
+ <Adapter slot="1" enabled="true" MACAddress="0800278CC821" type="82540EM">
60
+ <DisabledModes>
61
+ <NAT localhost-reachable="true"/>
62
+ <InternalNetwork name="intnet"/>
63
+ <NATNetwork name="NatNetwork"/>
64
+ </DisabledModes>
65
+ <HostOnlyInterface name="vboxnet0"/>
66
+ </Adapter>
67
+ </Network>
68
+ <AudioAdapter driver="ALSA"/>
69
+ <Clipboard mode="Bidirectional"/>
70
+ <StorageControllers>
71
+ <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true">
72
+ <AttachedDevice type="HardDisk" hotpluggable="false" port="0" device="0">
73
+ <Image uuid="{0898f36f-01c3-4b43-8aeb-36ba7adaef95}"/>
74
+ </AttachedDevice>
75
+ </StorageController>
76
+ </StorageControllers>
77
+ <RTC localOrUTC="UTC"/>
78
+ <CPU count="2">
79
+ <HardwareVirtExLargePages enabled="false"/>
80
+ <PAE enabled="true"/>
81
+ <LongMode enabled="true"/>
82
+ </CPU>
83
+ </Hardware>
84
+ <Snapshots>
85
+ <Snapshot uuid="{264ccd7e-9ffd-45ba-bd0e-4e1968d3355a}" name="push_1766170151_8843" timeStamp="2025-12-19T18:49:11Z">
86
+ <Hardware>
87
+ <Memory RAMSize="3000"/>
88
+ <Boot>
89
+ <Order position="1" device="HardDisk"/>
90
+ <Order position="2" device="DVD"/>
91
+ <Order position="3" device="None"/>
92
+ <Order position="4" device="None"/>
93
+ </Boot>
94
+ <Display VRAMSize="128"/>
95
+ <RemoteDisplay enabled="true">
96
+ <VRDEProperties>
97
+ <Property name="TCP/Address" value="127.0.0.1"/>
98
+ <Property name="TCP/Ports" value="5902"/>
99
+ </VRDEProperties>
100
+ </RemoteDisplay>
101
+ <Firmware/>
102
+ <BIOS>
103
+ <IOAPIC enabled="true"/>
104
+ <SmbiosUuidLittleEndian enabled="true"/>
105
+ </BIOS>
106
+ <Network>
107
+ <Adapter slot="0" enabled="true" MACAddress="0800277AA2FC" type="82540EM">
108
+ <NAT localhost-reachable="true">
109
+ <DNS use-proxy="true"/>
110
+ <Forwarding name="ssh" proto="1" hostip="127.0.0.1" hostport="2222" guestport="22"/>
111
+ <Forwarding name="winrm" proto="1" hostip="127.0.0.1" hostport="55985" guestport="5985"/>
112
+ <Forwarding name="winrm-ssl" proto="1" hostip="127.0.0.1" hostport="55986" guestport="5986"/>
113
+ </NAT>
114
+ </Adapter>
115
+ <Adapter slot="1" enabled="true" MACAddress="0800278CC821" type="82540EM">
116
+ <DisabledModes>
117
+ <NAT localhost-reachable="true"/>
118
+ <InternalNetwork name="intnet"/>
119
+ <NATNetwork name="NatNetwork"/>
120
+ </DisabledModes>
121
+ <HostOnlyInterface name="vboxnet0"/>
122
+ </Adapter>
123
+ </Network>
124
+ <AudioAdapter driver="ALSA"/>
125
+ <Clipboard mode="Bidirectional"/>
126
+ <StorageControllers>
127
+ <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true">
128
+ <AttachedDevice type="HardDisk" hotpluggable="false" port="0" device="0">
129
+ <Image uuid="{3c72ec80-dc73-4448-a63e-97970cdd87e5}"/>
130
+ </AttachedDevice>
131
+ </StorageController>
132
+ </StorageControllers>
133
+ <RTC localOrUTC="UTC"/>
134
+ <CPU count="2">
135
+ <HardwareVirtExLargePages enabled="false"/>
136
+ <PAE enabled="true"/>
137
+ <LongMode enabled="true"/>
138
+ </CPU>
139
+ </Hardware>
140
+ </Snapshot>
141
+ </Snapshots>
142
+ </Snapshot>
143
+ <Hardware>
144
+ <Memory RAMSize="3000"/>
145
+ <Boot>
146
+ <Order position="1" device="HardDisk"/>
147
+ <Order position="2" device="DVD"/>
148
+ <Order position="3" device="None"/>
149
+ <Order position="4" device="None"/>
150
+ </Boot>
151
+ <Display VRAMSize="128"/>
152
+ <RemoteDisplay enabled="true">
153
+ <VRDEProperties>
154
+ <Property name="TCP/Address" value="127.0.0.1"/>
155
+ <Property name="TCP/Ports" value="5902"/>
156
+ </VRDEProperties>
157
+ </RemoteDisplay>
158
+ <Firmware/>
159
+ <BIOS>
160
+ <IOAPIC enabled="true"/>
161
+ <SmbiosUuidLittleEndian enabled="true"/>
162
+ </BIOS>
163
+ <Network>
164
+ <Adapter slot="0" enabled="true" MACAddress="0800277AA2FC" type="82540EM">
165
+ <NAT localhost-reachable="true">
166
+ <DNS use-proxy="true"/>
167
+ <Forwarding name="ssh" proto="1" hostip="127.0.0.1" hostport="2222" guestport="22"/>
168
+ <Forwarding name="winrm" proto="1" hostip="127.0.0.1" hostport="55985" guestport="5985"/>
169
+ <Forwarding name="winrm-ssl" proto="1" hostip="127.0.0.1" hostport="55986" guestport="5986"/>
170
+ </NAT>
171
+ </Adapter>
172
+ <Adapter slot="1" enabled="true" MACAddress="0800278CC821" type="82540EM">
173
+ <DisabledModes>
174
+ <NAT localhost-reachable="true"/>
175
+ <InternalNetwork name="intnet"/>
176
+ <NATNetwork name="NatNetwork"/>
177
+ </DisabledModes>
178
+ <HostOnlyInterface name="vboxnet0"/>
179
+ </Adapter>
180
+ </Network>
181
+ <AudioAdapter driver="ALSA"/>
182
+ <Clipboard mode="Bidirectional"/>
183
+ <StorageControllers>
184
+ <StorageController name="IDE Controller" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true">
185
+ <AttachedDevice type="HardDisk" hotpluggable="false" port="0" device="0">
186
+ <Image uuid="{e6800503-8273-4f16-b584-c7ba2c1df698}"/>
187
+ </AttachedDevice>
188
+ </StorageController>
189
+ </StorageControllers>
190
+ <RTC localOrUTC="UTC"/>
191
+ <CPU count="2">
192
+ <HardwareVirtExLargePages enabled="false"/>
193
+ <PAE enabled="true"/>
194
+ <LongMode enabled="true"/>
195
+ </CPU>
196
+ </Hardware>
197
+ <Groups>
198
+ <Group name="/GOAD"/>
199
+ </Groups>
200
+ </Machine>
201
+ </VirtualBox>
@@ -0,0 +1,103 @@
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ ** DO NOT EDIT THIS FILE.
4
+ ** If you make changes to this file while any VirtualBox related application
5
+ ** is running, your changes will be overwritten later, without taking effect.
6
+ ** Use VBoxManage or the VirtualBox Manager GUI to make changes.
7
+ **
8
+ ** Written by VirtualBox 7.2.4 (r170995)
9
+ -->
10
+ <VirtualBox xmlns="http://www.virtualbox.org/" version="1.19-linux">
11
+ <Machine uuid="{366f53dd-1710-434e-8021-1f91b0405b65}" name="encrypted test" OSType="Windows11_64" currentSnapshot="{cfbdc706-6561-44e8-8f3a-236df446b808}" snapshotFolder="Snapshots" lastStateChange="2026-01-20T13:01:05Z">
12
+ <MediaRegistry>
13
+ <HardDisks>
14
+ <HardDisk uuid="{24cdb8e9-35d6-42f2-aa17-c2d78bf1e1de}" location="encrypted test.vdi" format="VDI" type="Normal">
15
+ <Property name="CRYPT/KeyId" value="encrypted test"/>
16
+ <Property name="CRYPT/KeyStore" value="U0NORQABQUVTLVhUUzI1Ni1QTEFJTjY0AAAAAAAAAAAAAAAAAABQQktERjItU0hB&#10;MjU2AAAAAAAAAAAAAAAAAAAAAAAAAEAAAACOEfHKuPU9MHJktr3UZAxmuL+epJsk&#10;SRfK3vhbPnb4USAAAABCoUSl+kb0+6OzG8N3wo16bru7UNsjHzIsB+OPBfZUPiBO&#10;AACa0Cbl1wJz09E5i8U10V3xElnY8iAIwEqSwVWpwFPiYsAJDgBAAAAAgcb2cMho&#10;wWkDpje3pQHrn62sH8oTyr4+wXaJoFUEreymEuIXYlnB+jeLjQDWEEIs9DUIXfiP&#10;PWw3Fph9h0a3lA=="/>
17
+ <HardDisk uuid="{742e6d0f-8896-4aa6-97f4-e05d70e73029}" location="Snapshots/{742e6d0f-8896-4aa6-97f4-e05d70e73029}.vdi" format="VDI"/>
18
+ </HardDisk>
19
+ <HardDisk uuid="{1db0b9fe-36c2-44e8-9b7c-61fa5b6d1462}" location="encrypted test_1.vdi" format="VDI" type="Normal"/>
20
+ </HardDisks>
21
+ </MediaRegistry>
22
+ <Snapshot uuid="{cfbdc706-6561-44e8-8f3a-236df446b808}" name="Encryption" timeStamp="2026-01-20T13:01:05Z">
23
+ <Hardware>
24
+ <Memory RAMSize="4096"/>
25
+ <HID Pointing="USBTablet"/>
26
+ <Display controller="VBoxSVGA" VRAMSize="128"/>
27
+ <Firmware type="EFI"/>
28
+ <BIOS>
29
+ <IOAPIC enabled="true"/>
30
+ <NVRAM path="Snapshots/2026-01-20T13-01-05-717390000Z.nvram"/>
31
+ <SmbiosUuidLittleEndian enabled="true"/>
32
+ <AutoSerialNumGen enabled="true"/>
33
+ </BIOS>
34
+ <TrustedPlatformModule type="v2_0" location=""/>
35
+ <USB>
36
+ <Controllers>
37
+ <Controller name="XHCI" type="XHCI"/>
38
+ </Controllers>
39
+ </USB>
40
+ <Network>
41
+ <Adapter slot="0" enabled="true" MACAddress="0800274C9CFB" type="82540EM">
42
+ <NAT localhost-reachable="true"/>
43
+ </Adapter>
44
+ </Network>
45
+ <AudioAdapter controller="HDA" useDefault="true" driver="ALSA" enabled="true" enabledOut="true"/>
46
+ <Clipboard/>
47
+ <StorageControllers>
48
+ <StorageController name="SATA" type="AHCI" PortCount="2" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3">
49
+ <AttachedDevice type="HardDisk" hotpluggable="false" port="0" device="0">
50
+ <Image uuid="{24cdb8e9-35d6-42f2-aa17-c2d78bf1e1de}"/>
51
+ </AttachedDevice>
52
+ <AttachedDevice passthrough="false" type="DVD" hotpluggable="false" port="1" device="0"/>
53
+ </StorageController>
54
+ </StorageControllers>
55
+ <CPU count="2">
56
+ <HardwareVirtExLargePages enabled="false"/>
57
+ <PAE enabled="false"/>
58
+ <LongMode enabled="true"/>
59
+ </CPU>
60
+ </Hardware>
61
+ </Snapshot>
62
+ <Hardware>
63
+ <Memory RAMSize="4096"/>
64
+ <HID Pointing="USBTablet"/>
65
+ <Display controller="VBoxSVGA" VRAMSize="128"/>
66
+ <Firmware type="EFI"/>
67
+ <BIOS>
68
+ <IOAPIC enabled="true"/>
69
+ <SmbiosUuidLittleEndian enabled="true"/>
70
+ <AutoSerialNumGen enabled="true"/>
71
+ </BIOS>
72
+ <TrustedPlatformModule type="v2_0" location=""/>
73
+ <USB>
74
+ <Controllers>
75
+ <Controller name="XHCI" type="XHCI"/>
76
+ </Controllers>
77
+ </USB>
78
+ <Network>
79
+ <Adapter slot="0" enabled="true" MACAddress="0800274C9CFB" type="82540EM">
80
+ <NAT localhost-reachable="true"/>
81
+ </Adapter>
82
+ </Network>
83
+ <AudioAdapter controller="HDA" useDefault="true" driver="ALSA" enabled="true" enabledOut="true"/>
84
+ <Clipboard/>
85
+ <StorageControllers>
86
+ <StorageController name="SATA" type="AHCI" PortCount="3" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3">
87
+ <AttachedDevice type="HardDisk" hotpluggable="false" port="0" device="0">
88
+ <Image uuid="{742e6d0f-8896-4aa6-97f4-e05d70e73029}"/>
89
+ </AttachedDevice>
90
+ <AttachedDevice passthrough="false" type="DVD" hotpluggable="false" port="1" device="0"/>
91
+ <AttachedDevice type="HardDisk" hotpluggable="false" port="2" device="0">
92
+ <Image uuid="{1db0b9fe-36c2-44e8-9b7c-61fa5b6d1462}"/>
93
+ </AttachedDevice>
94
+ </StorageController>
95
+ </StorageControllers>
96
+ <CPU count="2">
97
+ <HardwareVirtExLargePages enabled="false"/>
98
+ <PAE enabled="false"/>
99
+ <LongMode enabled="true"/>
100
+ </CPU>
101
+ </Hardware>
102
+ </Machine>
103
+ </VirtualBox>
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+
6
+ def absolute_path(filename: str) -> Path:
7
+ return Path(__file__).parent.joinpath(filename).resolve()
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ from dissect.hypervisor.descriptor.hyperv import HyperVFile
4
+ from tests._util import absolute_path
5
+
6
+
7
+ def test_hyperv_vmcx() -> None:
8
+ with absolute_path("_data/descriptor/hyperv/test.vmcx").open("rb") as fh:
9
+ hf = HyperVFile(fh)
10
+
11
+ assert hf.header is hf.headers[0]
12
+ assert hf.version == 0x400
13
+ assert len(hf.replay_logs) == 1
14
+ assert len(hf.object_tables) == 1
15
+ assert len(hf.key_tables) == 8
16
+
17
+ obj = hf.as_dict()
18
+ assert set(obj.keys()) == {"configuration"}
19
+ assert len(obj["configuration"].keys()) == 27
20
+ assert len(obj["configuration"]["manifest"].keys()) == 39
21
+ assert len(obj["configuration"]["properties"].keys()) == 11
22
+ assert len(obj["configuration"]["settings"].keys()) == 6
23
+
24
+
25
+ def test_hyperv_vmrs() -> None:
26
+ with absolute_path("_data/descriptor/hyperv/test.VMRS").open("rb") as fh:
27
+ hf = HyperVFile(fh)
28
+
29
+ assert hf.header is hf.headers[0]
30
+ assert hf.version == 0x400
31
+ assert len(hf.replay_logs) == 1
32
+ assert len(hf.object_tables) == 1
33
+ assert len(hf.key_tables) == 2
34
+
35
+ obj = hf.as_dict()
36
+ target = {
37
+ "configuration": {
38
+ "properties": {"version": 2304},
39
+ "global_settings": {
40
+ "metrics": {
41
+ "devicetype": {
42
+ "guid": "83F8638B-8DCA-4152-9EDA-2CA8B33039B4",
43
+ "deviceinstance": {
44
+ "guid": "83F8638B-8DCA-4152-9EDA-2CA8B33039B4",
45
+ "metric": {
46
+ "typecode": "4E1D459F-7861-46A4-887C-B64397C97E1B;0\\0\\L",
47
+ "value": 0,
48
+ "enabled": False,
49
+ "starttime": 0,
50
+ "lastcomputedtime": 0,
51
+ "peaktime": 0,
52
+ "poolid": "",
53
+ "resourcetypeid": "70BB60D2-A9D3-46AA-B654-3DE53004B4F8",
54
+ },
55
+ },
56
+ }
57
+ }
58
+ },
59
+ "_ac6b8dc1-3257-4a70-b1b2-a9c9215659ad_": {"VDEVVersion": 2048},
60
+ "_e51b7ef6-4a7f-4780-aaae-d4b291aacd2e_": {"VDEVVersion": 512},
61
+ "_83f8638b-8dca-4152-9eda-2ca8b33039b4_": {"VDEVVersion": 1792},
62
+ }
63
+ }
64
+ assert obj == target