VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/tests/api/tdTreeDepth1.py@ 99100

Last change on this file since 99100 was 99100, checked in by vboxsync, 2 years ago

ValidationKit/tests/api/tdTreeDepth1.py: Rather than include a delay
which waits for Machine::uninit() to be called to close the attached
media after the reference count of the IMachine object ('oVM') has
dropped to zero we can instead trigger Python's garbage collector
directly (gc.collect()) which avoids the need for waiting.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: tdTreeDepth1.py 99100 2023-03-21 17:25:26Z vboxsync $
4
5"""
6VirtualBox Validation Kit - Medium and Snapshot Tree Depth Test #1
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 2010-2023 Oracle and/or its affiliates.
12
13This file is part of VirtualBox base platform packages, as
14available from https://www.215389.xyz.
15
16This program is free software; you can redistribute it and/or
17modify it under the terms of the GNU General Public License
18as published by the Free Software Foundation, in version 3 of the
19License.
20
21This program is distributed in the hope that it will be useful, but
22WITHOUT ANY WARRANTY; without even the implied warranty of
23MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24General Public License for more details.
25
26You should have received a copy of the GNU General Public License
27along with this program; if not, see <https://www.gnu.org/licenses>.
28
29The contents of this file may alternatively be used under the terms
30of the Common Development and Distribution License Version 1.0
31(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
32in the VirtualBox distribution, in which case the provisions of the
33CDDL are applicable instead of those of the GPL.
34
35You may elect to license modified versions of this file under the
36terms and conditions of either the GPL or the CDDL or both.
37
38SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
39"""
40__version__ = "$Revision: 99100 $"
41
42
43# Standard Python imports.
44import os
45import sys
46import random
47import gc
48
49# Only the main script needs to modify the path.
50try: __file__ # pylint: disable=used-before-assignment
51except: __file__ = sys.argv[0]
52g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
53sys.path.append(g_ksValidationKitDir)
54
55# Validation Kit imports.
56from testdriver import base
57from testdriver import reporter
58from testdriver import vboxcon
59
60
61class SubTstDrvTreeDepth1(base.SubTestDriverBase):
62 """
63 Sub-test driver for Medium and Snapshot Tree Depth Test #1.
64 """
65
66 def __init__(self, oTstDrv):
67 base.SubTestDriverBase.__init__(self, oTstDrv, 'tree-depth', 'Media and Snapshot tree depths');
68
69 def testIt(self):
70 """
71 Execute the sub-testcase.
72 """
73 return self.testMediumTreeDepth() \
74 and self.testSnapshotTreeDepth();
75
76 def getNumOfAttachedHDs(self, oVM, sController):
77 """
78 Helper routine for counting the hard disks, including differencing disks,
79 attached to the specified countroller.
80 """
81 try:
82 aoMediumAttachments = oVM.getMediumAttachmentsOfController(sController);
83 except:
84 reporter.errorXcpt('oVM.getMediumAttachmentsOfController("%s") failed' % (sController));
85 return -1;
86
87 cDisks = 0;
88 for oAttachment in aoMediumAttachments:
89 oMedium = oAttachment.medium;
90 if oMedium.deviceType is vboxcon.DeviceType_HardDisk:
91 cDisks = cDisks + 1;
92 reporter.log2('base medium = %s cDisks = %d' % (oMedium.location, cDisks));
93 oParentMedium = oMedium.parent;
94 while oParentMedium is not None:
95 cDisks = cDisks + 1;
96 reporter.log2('parent medium = %s cDisks = %d' % (oParentMedium.location, cDisks));
97 oParentMedium = oParentMedium.parent;
98 return cDisks;
99
100 def unregisterVM(self, oVM, enmCleanupMode):
101 """
102 Helper routine to unregister the VM using the CleanupMode specified.
103 """
104 reporter.log('unregistering VM using %s' % enmCleanupMode);
105 if enmCleanupMode == 'DetachAllReturnHardDisksOnly':
106 try:
107 aoHDs = oVM.unregister(vboxcon.CleanupMode_DetachAllReturnHardDisksOnly);
108 except:
109 return reporter.logXcpt('unregister(CleanupMode_DetachAllReturnHardDisksOnly) failed');
110 for oHD in aoHDs:
111 reporter.log2('closing medium = %s' % (oHD.location));
112 oHD.close();
113 aoHDs = None;
114 elif enmCleanupMode == 'UnregisterOnly':
115 try:
116 aoHDs = oVM.unregister(vboxcon.CleanupMode_UnregisterOnly);
117 except:
118 return reporter.logXcpt('unregister(CleanupMode_UnregisterOnly) failed');
119 if aoHDs:
120 return reporter.error('unregister(CleanupMode_UnregisterOnly) failed: returned %d disks' %
121 len(aoHDs));
122 else:
123 return reporter.error('unregisterVM: unexpected CleanupMode "%s"' % enmCleanupMode);
124
125 return True;
126
127 def openAndRegisterMachine(self, oVBox, sSettingsFile):
128 """
129 Helper routine which opens a VM and registers it.
130 """
131 reporter.log('opening VM using configuration file = %s, testing config reading' % (sSettingsFile));
132 try:
133 if self.oTstDrv.fpApiVer >= 7.0:
134 # Needs a password parameter since 7.0.
135 oVM = oVBox.openMachine(sSettingsFile, "");
136 else:
137 oVM = oVBox.openMachine(sSettingsFile);
138 except:
139 reporter.logXcpt('openMachine(%s) failed' % (sSettingsFile));
140 return None;
141
142 if not oVM:
143 return None;
144
145 try:
146 oVBox.registerMachine(oVM);
147 except:
148 reporter.logXcpt('registerMachine(%s) failed' % (sSettingsFile));
149 return None;
150
151 return oVM;
152
153 #
154 # Test execution helpers.
155 #
156
157 def testMediumTreeDepth(self):
158 """
159 Test medium tree depth.
160 """
161 reporter.testStart('mediumTreeDepth')
162
163 oVM = self.oTstDrv.createTestVMOnly('test-medium', 'Other')
164 if oVM is None:
165 return False;
166
167 # Save the path to the VM's settings file while oVM is valid (needed for
168 # openMachine() later when oVM is gone).
169 sSettingsFile = oVM.settingsFilePath
170
171 oSession = self.oTstDrv.openSession(oVM)
172 if oSession is None:
173 return False;
174
175 # create chain with up to 64 disk images (medium tree depth limit)
176 cImages = random.randrange(1, 64);
177 reporter.log('Creating chain with %d disk images' % (cImages))
178 for i in range(1, cImages + 1):
179 sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'Test' + str(i) + '.vdi')
180 if i == 1:
181 oHd = oSession.createBaseHd(sHddPath, cb=1024*1024)
182 else:
183 oHd = oSession.createDiffHd(oHd, sHddPath)
184 if oHd is None:
185 return False;
186
187 # modify the VM config, attach HDD
188 sController='SATA Controller';
189 fRc = oSession.attachHd(sHddPath, sController, fImmutable=False, fForceResource=False);
190 if fRc:
191 fRc = oSession.saveSettings(fClose=True);
192 oSession = None;
193 if not fRc:
194 return False;
195
196 # Verify that the number of hard disks attached to the VM's only storage
197 # controller equals the number of disks created above.
198 cDisks = self.getNumOfAttachedHDs(oVM, sController);
199 reporter.log('Initial state: Number of hard disks attached = %d (should equal disks created = %d)' % (cDisks, cImages));
200 if cImages != cDisks:
201 reporter.error('Created %d disk images but found %d disks attached' % (cImages, cDisks));
202
203 # unregister the VM using "DetachAllReturnHardDisksOnly" and confirm all
204 # hard disks were returned and subsequently closed
205 fRc = self.unregisterVM(oVM, 'DetachAllReturnHardDisksOnly');
206 if not fRc:
207 return False;
208 oVM = None;
209
210 # If there is no base image (expected) then there are no leftover
211 # child images either.
212 oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox();
213 if oVBox is None:
214 return False;
215
216 cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
217 reporter.log('After unregister(DetachAllReturnHardDisksOnly): API reports %d base images' % (cBaseImages));
218 if cBaseImages != 0:
219 reporter.error('Got %d initial base images, expected zero (0)' % (cBaseImages));
220
221 # re-register to test loading of settings
222 oVM = self.openAndRegisterMachine(oVBox, sSettingsFile);
223 if oVM is None:
224 return False;
225
226 # Verify that the number of hard disks attached to the VM's only storage
227 # controller equals the number of disks created above.
228 cDisks = self.getNumOfAttachedHDs(oVM, sController);
229 reporter.log('After openMachine()+registerMachine(): Number of hard disks attached = %d '
230 '(should equal disks created = %d)' % (cDisks, cImages));
231 if cImages != cDisks:
232 reporter.error('Created %d disk images but after openMachine()+registerMachine() found %d disks attached' %
233 (cImages, cDisks));
234
235 fRc = self.unregisterVM(oVM, 'UnregisterOnly');
236 if not fRc:
237 return False;
238 oVM = None;
239
240 # When unregistering a VM with CleanupMode_UnregisterOnly the associated medium's
241 # objects will be closed when the corresponding Machine object ('oVM') is
242 # uninitialized. Assigning the 'None' object to 'oVM' will cause Python to delete
243 # the object when the garbage collector runs however this can take several seconds
244 # so we invoke the Python garbage collector manually here so we don't have to wait.
245 reporter.log('Invoking python garbage collection to trigger Machine::uninit() which will close the attached disks');
246 try:
247 gc.collect();
248 except:
249 reporter.logXcpt();
250
251 cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'));
252 reporter.log('After unregister(UnregisterOnly): API reports %d base images' % (cBaseImages));
253 if cBaseImages != 0:
254 reporter.error('Got %d base images after unregistering, expected zero (0)' % (cBaseImages));
255
256 return reporter.testDone()[1] == 0;
257
258 def testSnapshotTreeDepth(self):
259 """
260 Test snapshot tree depth.
261 """
262 reporter.testStart('snapshotTreeDepth')
263
264 oVM = self.oTstDrv.createTestVMOnly('test-snap', 'Other');
265 if oVM is None:
266 return False;
267
268 # Save the path to the VM's settings file while oVM is valid (needed for
269 # openMachine() later when oVM is gone).
270 sSettingsFile = oVM.settingsFilePath
271
272 # modify the VM config, create and attach empty HDD
273 oSession = self.oTstDrv.openSession(oVM)
274 if oSession is None:
275 return False;
276 sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'TestSnapEmpty.vdi')
277 sController='SATA Controller';
278 fRc = oSession.createAndAttachHd(sHddPath, cb=1024*1024, sController=sController, fImmutable=False);
279 fRc = fRc and oSession.saveSettings();
280 if not fRc:
281 return False;
282
283 # take up to 200 snapshots (250 is the snapshot tree depth limit (settings.h:SETTINGS_SNAPSHOT_DEPTH_MAX))
284 cSnapshots = random.randrange(1, 200);
285 reporter.log('Taking %d snapshots' % (cSnapshots));
286 for i in range(1, cSnapshots + 1):
287 fRc = oSession.takeSnapshot('Snapshot ' + str(i));
288 if not fRc:
289 return False;
290 fRc = oSession.close() and fRc and True;
291 oSession = None;
292 reporter.log('API reports %d snapshots (should equal snapshots created = %d)' % (oVM.snapshotCount, cSnapshots));
293 if oVM.snapshotCount != cSnapshots:
294 reporter.error('Got %d initial snapshots, expected %d' % (oVM.snapshotCount, cSnapshots));
295
296 # unregister the VM using "DetachAllReturnHardDisksOnly" and confirm all
297 # hard disks were returned and subsequently closed
298 fRc = self.unregisterVM(oVM, 'DetachAllReturnHardDisksOnly');
299 if not fRc:
300 return False;
301 oVM = None;
302
303 # If there is no base image (expected) then there are no leftover
304 # child images either.
305 oVBox = self.oTstDrv.oVBoxMgr.getVirtualBox()
306 if oVBox is None:
307 return False;
308
309 cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
310 reporter.log('After unregister(DetachAllReturnHardDisksOnly): API reports %d base images' % (cBaseImages));
311 fRc = fRc and cBaseImages == 0
312 if cBaseImages != 0:
313 reporter.error('Got %d initial base images, expected zero (0)' % (cBaseImages));
314
315 # re-register to test loading of settings
316 oVM = self.openAndRegisterMachine(oVBox, sSettingsFile);
317 if oVM is None:
318 return False;
319
320 # Verify that the number of hard disks attached to the VM's only storage
321 # controller equals the number of disks created above.
322 reporter.log('After openMachine()+registerMachine(): Number of snapshots of VM = %d '
323 '(should equal snapshots created = %d)' % (oVM.snapshotCount, cSnapshots));
324 if oVM.snapshotCount != cSnapshots:
325 reporter.error('Created %d snapshots but after openMachine()+registerMachine() found %d snapshots' %
326 (cSnapshots, oVM.snapshotCount));
327
328 fRc = self.unregisterVM(oVM, 'UnregisterOnly');
329 if not fRc:
330 return False;
331 oVM = None;
332
333 # When unregistering a VM with CleanupMode_UnregisterOnly the associated medium's
334 # objects will be closed when the corresponding Machine object ('oVM') is
335 # uninitialized. Assigning the 'None' object to 'oVM' will cause Python to delete
336 # the object when the garbage collector runs however this can take several seconds
337 # so we invoke the Python garbage collector manually here so we don't have to wait.
338 reporter.log('Invoking python garbage collection to trigger Machine::uninit() which will close the attached disks');
339 try:
340 gc.collect();
341 except:
342 reporter.logXcpt();
343
344 cBaseImages = len(self.oTstDrv.oVBoxMgr.getArray(oVBox, 'hardDisks'))
345 reporter.log('After unregister(UnregisterOnly): API reports %d base images' % (cBaseImages));
346 if cBaseImages != 0:
347 reporter.error('Got %d base images after unregistering, expected zero (0)' % (cBaseImages));
348
349 return reporter.testDone()[1] == 0
350
351
352if __name__ == '__main__':
353 sys.path.append(os.path.dirname(os.path.abspath(__file__)))
354 from tdApi1 import tdApi1; # pylint: disable=relative-import
355 sys.exit(tdApi1([SubTstDrvTreeDepth1]).main(sys.argv))
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette