VirtualBox

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

Last change on this file since 103914 was 99123, checked in by vboxsync, 21 months ago

ValidationKit/tests/api/tdTreeDepth1.py: If all of the API tests are
run via tdApi1.py then VMs created before tdTreeDepth1.py which
don't clean up after themselves will have populated the global
IVirtualBox::hardDisks[] array which this test examines for any base
media leftover after calling IMachine::unregister(). The check here
needs smartening up to verify whether any of the base hard disks in the
HardDisks[] array actually belong to this VM.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 16.4 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: tdTreeDepth1.py 99123 2023-03-22 21:40:58Z 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.virtualbox.org.
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: 99123 $"
41
42
43# Standard Python imports.
44import os
45import sys
46import random
47import time
48import gc
49
50# Only the main script needs to modify the path.
51try: __file__ # pylint: disable=used-before-assignment
52except: __file__ = sys.argv[0]
53g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
54sys.path.append(g_ksValidationKitDir)
55
56# Validation Kit imports.
57from testdriver import base
58from testdriver import reporter
59from testdriver import vboxcon
60
61
62class SubTstDrvTreeDepth1(base.SubTestDriverBase):
63 """
64 Sub-test driver for Medium and Snapshot Tree Depth Test #1.
65 """
66
67 def __init__(self, oTstDrv):
68 base.SubTestDriverBase.__init__(self, oTstDrv, 'tree-depth', 'Media and Snapshot tree depths');
69
70 def testIt(self):
71 """
72 Execute the sub-testcase.
73 """
74 return self.testMediumTreeDepth() \
75 and self.testSnapshotTreeDepth();
76
77 def getNumOfAttachedHDs(self, oVM, sController):
78 """
79 Helper routine for counting the hard disks, including differencing disks,
80 attached to the specified countroller.
81 """
82 try:
83 aoMediumAttachments = oVM.getMediumAttachmentsOfController(sController);
84 except:
85 reporter.errorXcpt('oVM.getMediumAttachmentsOfController("%s") failed' % (sController));
86 return -1;
87
88 cDisks = 0;
89 for oAttachment in aoMediumAttachments:
90 oMedium = oAttachment.medium;
91 if oMedium.deviceType is vboxcon.DeviceType_HardDisk:
92 cDisks = cDisks + 1;
93 reporter.log2('base medium = %s cDisks = %d' % (oMedium.location, cDisks));
94 oParentMedium = oMedium.parent;
95 while oParentMedium is not None:
96 cDisks = cDisks + 1;
97 reporter.log2('parent medium = %s cDisks = %d' % (oParentMedium.location, cDisks));
98 oParentMedium = oParentMedium.parent;
99 return cDisks;
100
101 def getNumOfHDsFromHardDisksArray(self):
102 """
103 Helper routine for counting the hard disks that belong to this VM which are
104 contained in the global IVirtualBox::hardDisks[] array (which contains base media
105 only).
106 """
107 cDisks = 0;
108 aoHDs = self.oTstDrv.oVBoxMgr.getArray(self.oTstDrv.oVBox, 'hardDisks');
109 # Walk the IVirtualBox::hardDisks[] array for hard disks belonging to this VM.
110 # The array may contain entries from other VMs created by previous API tessts
111 # which haven't unregistered and deleted their VM's configuration. The location
112 # attribute of each disk looks similar to:
113 # /var/tmp/VBoxTestTmp/VBoxUserHome/Machines/vm 2/tdAppliance1-t2-disk1.vmdk
114 # So we compare the directory where the disk is located, i.e. the 'basefolder',
115 # with our VM's basefolder which looks similar to:
116 # /var/tmp/VBoxTestTmp/VBoxUserHome/Machines/test-medium
117 # to determine if the disk belongs to us.
118 for oHD in aoHDs:
119 sHDBaseFolder = os.path.dirname(oHD.location);
120 sVMBaseFolder = os.path.join(self.oTstDrv.oVBox.systemProperties.defaultMachineFolder, 'test-medium');
121 reporter.log2('HDBaseFolder = %s VMBaseFolder = %s' % (sHDBaseFolder, sVMBaseFolder));
122 if sHDBaseFolder== sVMBaseFolder:
123 cDisks = cDisks + 1;
124 return cDisks;
125
126 def unregisterVM(self, oVM, enmCleanupMode):
127 """
128 Helper routine to unregister the VM using the CleanupMode specified.
129 """
130 reporter.log('unregistering VM using %s' % enmCleanupMode);
131 if enmCleanupMode == 'DetachAllReturnHardDisksOnly':
132 try:
133 aoHDs = oVM.unregister(vboxcon.CleanupMode_DetachAllReturnHardDisksOnly);
134 except:
135 return reporter.logXcpt('unregister(CleanupMode_DetachAllReturnHardDisksOnly) failed');
136 for oHD in aoHDs:
137 reporter.log2('closing medium = %s' % (oHD.location));
138 oHD.close();
139 aoHDs = None;
140 elif enmCleanupMode == 'UnregisterOnly':
141 try:
142 aoHDs = oVM.unregister(vboxcon.CleanupMode_UnregisterOnly);
143 except:
144 return reporter.logXcpt('unregister(CleanupMode_UnregisterOnly) failed');
145 if aoHDs:
146 return reporter.error('unregister(CleanupMode_UnregisterOnly) failed: returned %d disks' %
147 len(aoHDs));
148 else:
149 return reporter.error('unregisterVM: unexpected CleanupMode "%s"' % enmCleanupMode);
150
151 return True;
152
153 def openAndRegisterMachine(self, sSettingsFile):
154 """
155 Helper routine which opens a VM and registers it.
156 """
157 reporter.log('opening VM using configuration file = %s, testing config reading' % (sSettingsFile));
158 try:
159 if self.oTstDrv.fpApiVer >= 7.0:
160 # Needs a password parameter since 7.0.
161 oVM = self.oTstDrv.oVBox.openMachine(sSettingsFile, "");
162 else:
163 oVM = self.oTstDrv.oVBox.openMachine(sSettingsFile);
164 except:
165 reporter.logXcpt('openMachine(%s) failed' % (sSettingsFile));
166 return None;
167
168 if not oVM:
169 return None;
170
171 try:
172 self.oTstDrv.oVBox.registerMachine(oVM);
173 except:
174 reporter.logXcpt('registerMachine(%s) failed' % (sSettingsFile));
175 return None;
176
177 return oVM;
178
179 #
180 # Test execution helpers.
181 #
182
183 def testMediumTreeDepth(self):
184 """
185 Test medium tree depth.
186 """
187 reporter.testStart('mediumTreeDepth')
188
189 oVM = self.oTstDrv.createTestVMOnly('test-medium', 'Other')
190 if oVM is None:
191 return False;
192
193 # Save the path to the VM's settings file while oVM is valid (needed for
194 # openMachine() later when oVM is gone).
195 sSettingsFile = oVM.settingsFilePath
196
197 oSession = self.oTstDrv.openSession(oVM)
198 if oSession is None:
199 return False;
200
201 # create chain with up to 64 disk images (medium tree depth limit)
202 cImages = random.randrange(1, 64);
203 reporter.log('Creating chain with %d disk images' % (cImages))
204 for i in range(1, cImages + 1):
205 sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'Test' + str(i) + '.vdi')
206 if i == 1:
207 oHd = oSession.createBaseHd(sHddPath, cb=1024*1024)
208 else:
209 oHd = oSession.createDiffHd(oHd, sHddPath)
210 if oHd is None:
211 return False;
212
213 # modify the VM config, attach HDD
214 sController='SATA Controller';
215 fRc = oSession.attachHd(sHddPath, sController, fImmutable=False, fForceResource=False);
216 if fRc:
217 fRc = oSession.saveSettings(fClose=True);
218 oSession = None;
219 if not fRc:
220 return False;
221
222 # Verify that the number of hard disks attached to the VM's only storage
223 # controller equals the number of disks created above.
224 cDisks = self.getNumOfAttachedHDs(oVM, sController);
225 reporter.log('Initial state: Number of hard disks attached = %d (should equal disks created = %d)' % (cDisks, cImages));
226 if cImages != cDisks:
227 reporter.error('Created %d disk images but found %d disks attached' % (cImages, cDisks));
228
229 # unregister the VM using "DetachAllReturnHardDisksOnly" and confirm all
230 # hard disks were returned and subsequently closed
231 fRc = self.unregisterVM(oVM, 'DetachAllReturnHardDisksOnly');
232 if not fRc:
233 return False;
234 oVM = None;
235
236 # If there are no base media belonging to this VM in the global IVirtualBox::hardDisks[]
237 # array (expected) then there are no leftover child images either.
238 cNumDisks = self.getNumOfHDsFromHardDisksArray();
239 reporter.log('After unregister(DetachAllReturnHardDisksOnly): API reports %d base images (should be zero)' % (cNumDisks));
240 if cNumDisks != 0:
241 reporter.error('Got %d initial base images, expected zero (0)' % (cNumDisks));
242
243 # re-register to test loading of settings
244 oVM = self.openAndRegisterMachine(sSettingsFile);
245 if oVM is None:
246 return False;
247
248 # Verify that the number of hard disks attached to the VM's only storage
249 # controller equals the number of disks created above.
250 cDisks = self.getNumOfAttachedHDs(oVM, sController);
251 reporter.log('After openMachine()+registerMachine(): Number of hard disks attached = %d '
252 '(should equal disks created = %d)' % (cDisks, cImages));
253 if cImages != cDisks:
254 reporter.error('Created %d disk images but after openMachine()+registerMachine() found %d disks attached' %
255 (cImages, cDisks));
256
257 fRc = self.unregisterVM(oVM, 'UnregisterOnly');
258 if not fRc:
259 return False;
260 oVM = None;
261
262 # When unregistering a VM with CleanupMode_UnregisterOnly the associated medium's
263 # objects will be closed when the corresponding Machine object ('oVM') is
264 # uninitialized. Assigning the 'None' object to 'oVM' will cause Python to delete
265 # the object when the garbage collector runs however this can take several seconds
266 # so we invoke the Python garbage collector manually here so we don't have to wait.
267 try:
268 gc.collect();
269 except:
270 reporter.logXcpt();
271
272 reporter.log('Waiting three seconds for Machine::uninit() to be called to close the attached disks');
273 # Fudge factor: Machine::uninit() will be invoked when the oVM object is processed
274 # by the garbage collector above but it may take a few moments to close up to 64
275 # disks.
276 time.sleep(3);
277
278 # If there are no base media belonging to this VM in the global IVirtualBox::hardDisks[]
279 # array (expected) then there are no leftover child images either.
280 cNumDisks = self.getNumOfHDsFromHardDisksArray();
281 reporter.log('After unregister(UnregisterOnly): API reports %d base images (should be zero)' % (cNumDisks));
282 if cNumDisks != 0:
283 reporter.error('Got %d base images after unregistering, expected zero (0)' % (cNumDisks));
284
285 return reporter.testDone()[1] == 0;
286
287 def testSnapshotTreeDepth(self):
288 """
289 Test snapshot tree depth.
290 """
291 reporter.testStart('snapshotTreeDepth')
292
293 oVM = self.oTstDrv.createTestVMOnly('test-snap', 'Other');
294 if oVM is None:
295 return False;
296
297 # Save the path to the VM's settings file while oVM is valid (needed for
298 # openMachine() later when oVM is gone).
299 sSettingsFile = oVM.settingsFilePath
300
301 # modify the VM config, create and attach empty HDD
302 oSession = self.oTstDrv.openSession(oVM)
303 if oSession is None:
304 return False;
305 sHddPath = os.path.join(self.oTstDrv.sScratchPath, 'TestSnapEmpty.vdi')
306 sController='SATA Controller';
307 fRc = oSession.createAndAttachHd(sHddPath, cb=1024*1024, sController=sController, fImmutable=False);
308 fRc = fRc and oSession.saveSettings();
309 if not fRc:
310 return False;
311
312 # take up to 200 snapshots (250 is the snapshot tree depth limit (settings.h:SETTINGS_SNAPSHOT_DEPTH_MAX))
313 cSnapshots = random.randrange(1, 200);
314 reporter.log('Taking %d snapshots' % (cSnapshots));
315 for i in range(1, cSnapshots + 1):
316 fRc = oSession.takeSnapshot('Snapshot ' + str(i));
317 if not fRc:
318 return False;
319 fRc = oSession.close() and fRc and True;
320 oSession = None;
321 reporter.log('API reports %d snapshots (should equal snapshots created = %d)' % (oVM.snapshotCount, cSnapshots));
322 if oVM.snapshotCount != cSnapshots:
323 reporter.error('Got %d initial snapshots, expected %d' % (oVM.snapshotCount, cSnapshots));
324
325 # unregister the VM using "DetachAllReturnHardDisksOnly" and confirm all
326 # hard disks were returned and subsequently closed
327 fRc = self.unregisterVM(oVM, 'DetachAllReturnHardDisksOnly');
328 if not fRc:
329 return False;
330 oVM = None;
331
332 # If there are no base media belonging to this VM in the global IVirtualBox::hardDisks[]
333 # array (expected) then there are no leftover child images either.
334 cNumDisks = self.getNumOfHDsFromHardDisksArray();
335 reporter.log('After unregister(DetachAllReturnHardDisksOnly): API reports %d base images (should be zero)' % (cNumDisks));
336 fRc = fRc and cNumDisks == 0
337 if cNumDisks != 0:
338 reporter.error('Got %d initial base images, expected zero (0)' % (cNumDisks));
339
340 # re-register to test loading of settings
341 oVM = self.openAndRegisterMachine(sSettingsFile);
342 if oVM is None:
343 return False;
344
345 # Verify that the number of hard disks attached to the VM's only storage
346 # controller equals the number of disks created above.
347 reporter.log('After openMachine()+registerMachine(): Number of snapshots of VM = %d '
348 '(should equal snapshots created = %d)' % (oVM.snapshotCount, cSnapshots));
349 if oVM.snapshotCount != cSnapshots:
350 reporter.error('Created %d snapshots but after openMachine()+registerMachine() found %d snapshots' %
351 (cSnapshots, oVM.snapshotCount));
352
353 fRc = self.unregisterVM(oVM, 'UnregisterOnly');
354 if not fRc:
355 return False;
356 oVM = None;
357
358 # When unregistering a VM with CleanupMode_UnregisterOnly the associated medium's
359 # objects will be closed when the corresponding Machine object ('oVM') is
360 # uninitialized. Assigning the 'None' object to 'oVM' will cause Python to delete
361 # the object when the garbage collector runs however this can take several seconds
362 # so we invoke the Python garbage collector manually here so we don't have to wait.
363 try:
364 gc.collect();
365 except:
366 reporter.logXcpt();
367
368 reporter.log('Waiting three seconds for Machine::uninit() to be called to close the attached disks');
369 # Fudge factor: Machine::uninit() will be invoked when the oVM object is processed
370 # by the garbage collector above but it may take a few moments to close up to 200
371 # snapshots.
372 time.sleep(3);
373
374 # If there are no base media belonging to this VM in the global IVirtualBox::hardDisks[]
375 # array (expected) then there are no leftover child images either.
376 cNumDisks = self.getNumOfHDsFromHardDisksArray();
377 reporter.log('After unregister(UnregisterOnly): API reports %d base images (should be zero)' % (cNumDisks));
378 if cNumDisks != 0:
379 reporter.error('Got %d base images after unregistering, expected zero (0)' % (cNumDisks));
380
381 return reporter.testDone()[1] == 0
382
383
384if __name__ == '__main__':
385 sys.path.append(os.path.dirname(os.path.abspath(__file__)))
386 from tdApi1 import tdApi1; # pylint: disable=relative-import
387 sys.exit(tdApi1([SubTstDrvTreeDepth1]).main(sys.argv))
Note: See TracBrowser for help on using the repository browser.

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