VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/tests/api/tdSnapshots1.py@ 100264

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

ValidationKit/tests/api/{Makefile.kmk,tdSnapshots1.py}: Add two new
tests involving nested online snapshots. The first exercises
IMachine::takeSnapshot(), IMachine::findSnapshot()), and
IMachine::restoreSnapshot() to verify that restoring and booting a
nested snapshot doesn't also delete that snapshot. The second exercises
IMachine::unregister() and IMachine::deleteConfig() to verify that
deleting a VM with multiple snapshots does indeed delete the snapshots
as well. These scenarios were both encountered in the past as
regressions and are described in xTracker #10252. bugref:10252.

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 11.5 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: tdSnapshots1.py 100264 2023-06-23 12:47:37Z vboxsync $
4
5"""
6VirtualBox Validation Kit - Nested Snapshot Restoration Test #1
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 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: 100264 $"
41
42
43# Standard Python imports.
44import os
45import sys
46
47# Only the main script needs to modify the path.
48try: __file__ # pylint: disable=used-before-assignment
49except: __file__ = sys.argv[0]
50g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
51sys.path.append(g_ksValidationKitDir)
52
53# Validation Kit imports.
54from testdriver import base
55from testdriver import reporter
56from testdriver import vboxcon
57
58
59class SubTstDrvNestedSnapshots1(base.SubTestDriverBase):
60 """
61 Sub-test driver for nested snapshot testing.
62 """
63 def __init__(self, oTstDrv):
64 base.SubTestDriverBase.__init__(self, oTstDrv, 'nested-snapshots', 'Nested Snapshot Testing');
65 self.sVmName = 'tst-nested-snapshots';
66 # Note that any VM can be used here as these tests simply involve taking
67 # online snapshots and restoring them. This OL6 image was chosen based purely
68 # on its location being in the 7.1 directory.
69 self.asRsrcs = ['7.1/ol-6u10-x86.vdi'];
70
71 #
72 # Overridden methods specified in the TestDriverBase class (testdriver/base.py).
73 #
74
75 # Handle the action to execute the test itself.
76 def testIt(self):
77 """
78 Execute the sub-testcases.
79 """
80 return self.testRestoreNestedSnapshot() \
81 and self.testDeleteSnapshots();
82
83 #
84 # Test execution helpers.
85 #
86 def testRestoreNestedSnapshot(self):
87 """
88 The scenario being exercised here is referenced in xTracker 10252 Comment #9
89 where the sequence of restoring a nested snapshot and then booting that restored
90 VM would accidentally delete that snapshot's saved state file during boot. So
91 here we perform the following steps to exercise this functionality:
92 + Take two online snapshots of the VM: 'alpha', and 'beta' (IMachine::takeSnapshot())
93 + Restore snapshot 'beta' (IMachine::restoreSnapshot())
94 + Boot and then poweroff the VM
95 + Verify snapshot 'beta' still exists (IMachine::findSnapshot())
96 """
97 reporter.testStart('Restore a nested snapshot and verify saved state file exists afterwards');
98
99 # Restoring an online snapshot requires an updated TXS (r157880 or later) for the
100 # TCP keep alive support added in r157875 thus it is essential that the
101 # ValidationKit ISO be mounted in the VM so that TXS can auto-update if needed.
102 reporter.log('Creating test VM: \'%s\'' % self.sVmName);
103 oVM = self.oTstDrv.createTestVM(self.sVmName, 1, sHd = '7.1/ol-6u10-x86.vdi',
104 sKind = 'Oracle6_64', fIoApic = True,
105 sDvdImage = self.oTstDrv.sVBoxValidationKitIso);
106 if oVM is None:
107 reporter.error('Error creating test VM: \'%s\'' % self.sVmName);
108
109 reporter.log('Starting test VM \'%s\' for the first time' % self.sVmName);
110 oSession, oTxsSession = self.oTstDrv.startVmAndConnectToTxsViaTcp(self.sVmName, fCdWait=True,
111 fNatForwardingForTxs = False);
112 if oSession is None or oTxsSession is None:
113 return reporter.error('Failed to start test VM: \'%s\'' % self.sVmName);
114
115 reporter.log('Guest VM \'%s\' successfully started. Connection to TXS service established.' % self.sVmName);
116 self.oTstDrv.addTask(oTxsSession);
117
118 # Take two online snapshots.
119 reporter.log('Taking two online snapshots of test VM: \'%s\'' % self.sVmName);
120 fRc = oSession.takeSnapshot('alpha');
121 fRc = fRc and oSession.takeSnapshot('beta');
122
123 # Shutdown the VM and cleanup.
124 self.oTstDrv.txsDisconnect(oSession, oTxsSession)
125 reporter.log('Shutting down test VM: \'%s\'' % self.sVmName);
126 self.oTstDrv.removeTask(oTxsSession);
127 self.oTstDrv.terminateVmBySession(oSession);
128 fRc = oSession.close() and fRc and True; # pychecker hack.
129 oSession = None;
130 oTxsSession = None;
131 if not fRc:
132 return reporter.error('Failed to take snapshot of test VM: \'%s\'' % self.sVmName);
133
134 oVM = self.oTstDrv.getVmByName(self.sVmName);
135 oSession = self.oTstDrv.openSession(oVM);
136 if oSession is None:
137 return reporter.error('Failed to create session for test VM: \'%s\'' % self.sVmName);
138
139 oSnapshot = oSession.findSnapshot('beta');
140 if oSnapshot is None:
141 return reporter.testFailure('Failed to find snapshot \'beta\' of test VM: \'%s\'' % self.sVmName);
142
143 reporter.log('Restoring nested snapshot \'%s\' ({%s}) of test VM: \'%s\'' %
144 (oSnapshot.name, oSnapshot.id, self.sVmName));
145 fRc = oSession.restoreSnapshot(oSnapshot);
146 if not fRc:
147 return reporter.error('Failed to restore snapshot \'%s\' of test VM: \'%s\'' % (oSnapshot.name, self.sVmName));
148
149 fRc = oSession.close() and fRc and True; # pychecker hack.
150 oSession = None;
151 if not fRc:
152 return reporter.error('Failed to close session of test VM: \'%s\'' % self.sVmName);
153
154 reporter.log('Starting test VM after snapshot restore: \'%s\'' % self.sVmName);
155
156 oSession, oTxsSession = self.oTstDrv.startVmAndConnectToTxsViaTcp(self.sVmName, fCdWait=True,
157 fNatForwardingForTxs = False);
158 if oSession is None or oTxsSession is None:
159 return reporter.error('Failed to start test VM: \'%s\'' % self.sVmName);
160
161 # Display the version of TXS running in the guest VM to confirm that it is r157880 or later.
162 sTxsVer = self.oTstDrv.txsVer(oSession, oTxsSession, cMsTimeout=1000*30*60, fIgnoreErrors = True);
163 if sTxsVer is not False:
164 reporter.log('startVmAndConnectToTxsViaTcp: TestExecService version %s' % (sTxsVer));
165 else:
166 reporter.log('startVmAndConnectToTxsViaTcp: Unable to retrieve TestExecService version');
167
168 reporter.log('Guest VM \'%s\' successfully started after restoring nested snapshot.' % self.sVmName);
169 self.oTstDrv.addTask(oTxsSession);
170
171 # Shutdown the VM and cleanup.
172 reporter.log('Shutting down test VM: \'%s\'' % self.sVmName);
173 self.oTstDrv.removeTask(oTxsSession);
174 self.oTstDrv.terminateVmBySession(oSession);
175 fRc = oSession.close() and fRc and True; # pychecker hack.
176 oSession = None;
177 oTxsSession = None;
178 if not fRc:
179 return reporter.testFailure('Failed to close session of test VM: \'%s\'' % self.sVmName);
180
181 reporter.log('Verifying nested snapshot \'beta\' still exists.');
182 oVM = self.oTstDrv.getVmByName(self.sVmName);
183 oSession = self.oTstDrv.openSession(oVM);
184 if oSession is None:
185 return reporter.error('Failed to create session for test VM: \'%s\'' % self.sVmName);
186
187 oSnapshot = oSession.findSnapshot('beta');
188 if oSnapshot is None:
189 return reporter.testFailure('Failed to find snapshot \'beta\' of test VM: \'%s\'' % self.sVmName);
190
191 return reporter.testDone()[1] == 0;
192
193
194 def testDeleteSnapshots(self):
195 """
196 The scenario being exercised here is also referenced in xTracker 10252 Comment #9
197 where unregistering and deleting a VM which contained one or more snapshots would
198 neglect to delete the snapshot(s). So here we perform the following steps to
199 exercise this functionality which conveniently also tidies up our test setup:
200 + Unregister our test VM (IMachine::unregister())
201 + Delete the VM and the attached media including snapshots (IMachine::deleteConfig())
202 + Check that the snapshots are truly gone.
203 """
204
205 reporter.testStart('Verifying IMachine::unregister()+IMachine::deleteConfig() deletes snapshots');
206 oVM = self.oTstDrv.getVmByName(self.sVmName);
207 # IMachine::stateFilePath() isn't implemented in the testdriver so we manually
208 # retrieve the paths to the snapshots.
209 asStateFilesList = [];
210 sSnapshotFolder = oVM.snapshotFolder;
211 for sFile in os.listdir(sSnapshotFolder):
212 if sFile.endswith(".sav"):
213 sSnapshotFullPath = os.path.normcase(os.path.join(sSnapshotFolder, sFile));
214 asStateFilesList.append(sSnapshotFullPath)
215 reporter.log("Snapshot path = %s" % (sSnapshotFullPath))
216
217 reporter.log('Calling IMachine::unregister() and IMachine::deleteConfig()');
218 # Call IMachine::unregister() and IMachine::deleteConfig() (or their historic
219 # equivalents) to remove all vestiges of the VM from the system.
220 if self.oTstDrv.fpApiVer >= 4.0:
221 try:
222 oVM.unregister(vboxcon.CleanupMode_Full);
223 except:
224 return reporter.error('Failed to unregister VM \'%s\'' % self.sVmName);
225 try:
226 if self.oTstDrv.fpApiVer >= 4.3:
227 oProgress = oVM.deleteConfig([]);
228 else:
229 oProgress = oVM.delete([]);
230 except:
231 return reporter.error('Failed to delete configuration of VM \'%s\'' % self.sVmName);
232 else:
233 self.oTstDrv.waitOnProgress(oProgress);
234 else:
235 try:
236 self.oTstDrv.oVBox.unregisterMachine(oVM.id);
237 except:
238 return reporter.error('Failed to unregister VM \'%s\'' % self.sVmName);
239 try:
240 oVM.deleteSettings();
241 except:
242 return reporter.error('Failed to delete configuration of VM \'%s\'' % self.sVmName);
243
244 # Verify that all of the snapshots were removed as part of the
245 # IMachine::deleteConfig() call.
246 reporter.log('Verifying no snapshots remain after IMachine::deleteConfig()');
247 for sFile in os.listdir(sSnapshotFolder):
248 if os.path.exists(sFile):
249 return reporter.error('Snapshot \'%s\' was not deleted' % sFile);
250
251 return reporter.testDone()[1] == 0;
252
253if __name__ == '__main__':
254 sys.path.append(os.path.dirname(os.path.abspath(__file__)));
255 from tdApi1 import tdApi1; # pylint: disable=relative-import
256 sys.exit(tdApi1([SubTstDrvNestedSnapshots1]).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