VirtualBox

source: vbox/trunk/src/VBox/Main/testcase/tstOVF.cpp

Last change on this file was 101472, checked in by vboxsync, 7 months ago

FE/VBoxManage,FE/Qt,Main/{VirtualBox.xidl,Appliance}: Add the ability to
export and import VMs which contain an NVMe storage controller.
bugref:10159 ticketref:19320

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.4 KB
Line 
1/* $Id: tstOVF.cpp 101472 2023-10-17 11:45:00Z vboxsync $ */
2/** @file
3 *
4 * tstOVF - testcases for OVF import and export
5 */
6
7/*
8 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29#include <VBox/com/VirtualBox.h>
30
31#include <VBox/com/com.h>
32#include <VBox/com/array.h>
33#include <VBox/com/string.h>
34#include <VBox/com/ErrorInfo.h>
35#include <VBox/com/errorprint.h>
36
37#include <iprt/initterm.h>
38#include <iprt/stream.h>
39#include <iprt/file.h>
40#include <iprt/path.h>
41#include <iprt/param.h>
42
43#include <list>
44
45using namespace com;
46
47// main
48///////////////////////////////////////////////////////////////////////////////
49
50/**
51 * Quick hack exception structure.
52 *
53 */
54struct MyError
55{
56 MyError(HRESULT rc,
57 const char *pcsz,
58 IProgress *pProgress = NULL)
59 : m_rc(rc)
60 {
61 m_str = "ERROR: ";
62 m_str += pcsz;
63
64 if (pProgress)
65 {
66 com::ProgressErrorInfo info(pProgress);
67 com::GluePrintErrorInfo(info);
68 }
69 else if (rc != S_OK)
70 {
71 com::ErrorInfo info;
72 if (!info.isFullAvailable() && !info.isBasicAvailable())
73 com::GluePrintRCMessage(rc);
74 else
75 com::GluePrintErrorInfo(info);
76 }
77 }
78
79 Utf8Str m_str;
80 HRESULT m_rc;
81};
82
83/**
84 * Imports the given OVF file, with all bells and whistles.
85 * Throws MyError on errors.
86 * @param pcszPrefix Descriptive short prefix string for console output.
87 * @param pVirtualBox VirtualBox instance.
88 * @param pcszOVF0 File to import.
89 * @param llMachinesCreated out: UUIDs of machines that were created so that caller can clean up.
90 */
91static void importOVF(const char *pcszPrefix,
92 ComPtr<IVirtualBox> &pVirtualBox,
93 const char *pcszOVF0,
94 std::list<Guid> &llMachinesCreated)
95{
96 char szAbsOVF[RTPATH_MAX];
97 RTPathExecDir(szAbsOVF, sizeof(szAbsOVF));
98 RTPathAppend(szAbsOVF, sizeof(szAbsOVF), pcszOVF0);
99
100 RTPrintf("%s: reading appliance \"%s\"...\n", pcszPrefix, szAbsOVF);
101 ComPtr<IAppliance> pAppl;
102 HRESULT rc = pVirtualBox->CreateAppliance(pAppl.asOutParam());
103 if (FAILED(rc)) throw MyError(rc, "failed to create appliance\n");
104
105 ComPtr<IProgress> pProgress;
106 rc = pAppl->Read(Bstr(szAbsOVF).raw(), pProgress.asOutParam());
107 if (FAILED(rc)) throw MyError(rc, "Appliance::Read() failed\n");
108 rc = pProgress->WaitForCompletion(-1);
109 if (FAILED(rc)) throw MyError(rc, "Progress::WaitForCompletion() failed\n");
110 LONG rc2;
111 pProgress->COMGETTER(ResultCode)(&rc2);
112 if (FAILED(rc2)) throw MyError(rc2, "Appliance::Read() failed\n", pProgress);
113
114 RTPrintf("%s: interpreting appliance \"%s\"...\n", pcszPrefix, szAbsOVF);
115 rc = pAppl->Interpret();
116 if (FAILED(rc)) throw MyError(rc, "Appliance::Interpret() failed\n");
117
118 com::SafeIfaceArray<IVirtualSystemDescription> aDescriptions;
119 rc = pAppl->COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aDescriptions));
120 for (uint32_t u = 0;
121 u < aDescriptions.size();
122 ++u)
123 {
124 ComPtr<IVirtualSystemDescription> pVSys = aDescriptions[u];
125
126 com::SafeArray<VirtualSystemDescriptionType_T> aTypes;
127 com::SafeArray<BSTR> aRefs;
128 com::SafeArray<BSTR> aOvfValues;
129 com::SafeArray<BSTR> aVBoxValues;
130 com::SafeArray<BSTR> aExtraConfigValues;
131 rc = pVSys->GetDescription(ComSafeArrayAsOutParam(aTypes),
132 ComSafeArrayAsOutParam(aRefs),
133 ComSafeArrayAsOutParam(aOvfValues),
134 ComSafeArrayAsOutParam(aVBoxValues),
135 ComSafeArrayAsOutParam(aExtraConfigValues));
136 if (FAILED(rc)) throw MyError(rc, "VirtualSystemDescription::GetDescription() failed\n");
137
138 for (uint32_t u2 = 0;
139 u2 < aTypes.size();
140 ++u2)
141 {
142 const char *pcszType;
143
144 VirtualSystemDescriptionType_T t = aTypes[u2];
145 switch (t)
146 {
147 case VirtualSystemDescriptionType_OS:
148 pcszType = "ostype";
149 break;
150
151 case VirtualSystemDescriptionType_Name:
152 pcszType = "name";
153 break;
154
155 case VirtualSystemDescriptionType_Product:
156 pcszType = "product";
157 break;
158
159 case VirtualSystemDescriptionType_ProductUrl:
160 pcszType = "producturl";
161 break;
162
163 case VirtualSystemDescriptionType_Vendor:
164 pcszType = "vendor";
165 break;
166
167 case VirtualSystemDescriptionType_VendorUrl:
168 pcszType = "vendorurl";
169 break;
170
171 case VirtualSystemDescriptionType_Version:
172 pcszType = "version";
173 break;
174
175 case VirtualSystemDescriptionType_Description:
176 pcszType = "description";
177 break;
178
179 case VirtualSystemDescriptionType_License:
180 pcszType = "license";
181 break;
182
183 case VirtualSystemDescriptionType_CPU:
184 pcszType = "cpu";
185 break;
186
187 case VirtualSystemDescriptionType_Memory:
188 pcszType = "memory";
189 break;
190
191 case VirtualSystemDescriptionType_HardDiskControllerIDE:
192 pcszType = "ide";
193 break;
194
195 case VirtualSystemDescriptionType_HardDiskControllerSATA:
196 pcszType = "sata";
197 break;
198
199 case VirtualSystemDescriptionType_HardDiskControllerSAS:
200 pcszType = "sas";
201 break;
202
203 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
204 pcszType = "scsi";
205 break;
206
207 case VirtualSystemDescriptionType_HardDiskControllerVirtioSCSI:
208 pcszType = "virtio-scsi";
209 break;
210
211 case VirtualSystemDescriptionType_HardDiskControllerNVMe:
212 pcszType = "nvme";
213 break;
214
215 case VirtualSystemDescriptionType_HardDiskImage:
216 pcszType = "hd";
217 break;
218
219 case VirtualSystemDescriptionType_CDROM:
220 pcszType = "cdrom";
221 break;
222
223 case VirtualSystemDescriptionType_Floppy:
224 pcszType = "floppy";
225 break;
226
227 case VirtualSystemDescriptionType_NetworkAdapter:
228 pcszType = "net";
229 break;
230
231 case VirtualSystemDescriptionType_USBController:
232 pcszType = "usb";
233 break;
234
235 case VirtualSystemDescriptionType_SoundCard:
236 pcszType = "sound";
237 break;
238
239 case VirtualSystemDescriptionType_SettingsFile:
240 pcszType = "settings";
241 break;
242
243 case VirtualSystemDescriptionType_BaseFolder:
244 pcszType = "basefolder";
245 break;
246
247 case VirtualSystemDescriptionType_PrimaryGroup:
248 pcszType = "primarygroup";
249 break;
250
251 default:
252 throw MyError(E_UNEXPECTED, Utf8StrFmt("Invalid VirtualSystemDescriptionType (enum=%d)\n", t).c_str());
253 break;
254 }
255
256 RTPrintf(" vsys %2u item %2u: type %2d (%s), ovf: \"%ls\", vbox: \"%ls\", extra: \"%ls\"\n",
257 u, u2, t, pcszType,
258 aOvfValues[u2],
259 aVBoxValues[u2],
260 aExtraConfigValues[u2]);
261 }
262 }
263
264 RTPrintf("%s: importing %d machine(s)...\n", pcszPrefix, aDescriptions.size());
265 SafeArray<ImportOptions_T> sfaOptions;
266 rc = pAppl->ImportMachines(ComSafeArrayAsInParam(sfaOptions), pProgress.asOutParam());
267 if (FAILED(rc)) throw MyError(rc, "Appliance::ImportMachines() failed\n");
268 rc = pProgress->WaitForCompletion(-1);
269 if (FAILED(rc)) throw MyError(rc, "Progress::WaitForCompletion() failed\n");
270 pProgress->COMGETTER(ResultCode)(&rc2);
271 if (FAILED(rc2)) throw MyError(rc2, "Appliance::ImportMachines() failed\n", pProgress);
272
273 com::SafeArray<BSTR> aMachineUUIDs;
274 rc = pAppl->COMGETTER(Machines)(ComSafeArrayAsOutParam(aMachineUUIDs));
275 if (FAILED(rc)) throw MyError(rc, "Appliance::GetMachines() failed\n");
276
277 for (size_t u = 0;
278 u < aMachineUUIDs.size();
279 ++u)
280 {
281 RTPrintf("%s: created machine %u: %ls\n", pcszPrefix, u, aMachineUUIDs[u]);
282 llMachinesCreated.push_back(Guid(Bstr(aMachineUUIDs[u])));
283 }
284
285 RTPrintf("%s: success!\n", pcszPrefix);
286}
287
288/**
289 * Copies ovf-testcases/ovf-dummy.vmdk to the given target and appends that
290 * target as a string to the given list so that the caller can delete it
291 * again later.
292 * @param llFiles2Delete List of strings to append the target file path to.
293 * @param pcszDest Target for dummy VMDK.
294 */
295static void copyDummyDiskImage(const char *pcszPrefix,
296 std::list<Utf8Str> &llFiles2Delete,
297 const char *pcszDest)
298{
299 char szSrc[RTPATH_MAX];
300 RTPathExecDir(szSrc, sizeof(szSrc));
301 RTPathAppend(szSrc, sizeof(szSrc), "ovf-testcases/ovf-dummy.vmdk");
302
303 char szDst[RTPATH_MAX];
304 RTPathExecDir(szDst, sizeof(szDst));
305 RTPathAppend(szDst, sizeof(szDst), pcszDest);
306 RTPrintf("%s: copying ovf-dummy.vmdk to \"%s\"...\n", pcszPrefix, pcszDest);
307
308 /* Delete the destination file if it exists or RTFileCopy will fail. */
309 if (RTFileExists(szDst))
310 {
311 RTPrintf("Deleting file %s...\n", szDst);
312 RTFileDelete(szDst);
313 }
314
315 int vrc = RTFileCopy(szSrc, szDst);
316 if (RT_FAILURE(vrc)) throw MyError(0, Utf8StrFmt("Cannot copy ovf-dummy.vmdk to %s: %Rra\n", pcszDest, vrc).c_str());
317 llFiles2Delete.push_back(szDst);
318}
319
320/**
321 *
322 * @param argc
323 * @param argv[]
324 * @return
325 */
326int main(int argc, char *argv[])
327{
328 RTR3InitExe(argc, &argv, 0);
329
330 /** @todo r=aeichner: This should work for x86 machines on ARM platforms as well.
331 * Currently we get:
332 * tstOVF: error: x86-specific platform settings are not available on this platform
333 * tstOVF: error: Details: code VBOX_E_PLATFORM_ARCH_NOT_SUPPORTED (0x80bb0012), component PlatformWrap, interface IPlatform
334 * */
335
336 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
337 HRESULT rc = S_OK;
338
339 std::list<Utf8Str> llFiles2Delete;
340 std::list<Guid> llMachinesCreated;
341
342 ComPtr<IVirtualBoxClient> pVirtualBoxClient;
343 ComPtr<IVirtualBox> pVirtualBox;
344
345 try
346 {
347 RTPrintf("Initializing COM...\n");
348 rc = com::Initialize();
349 if (FAILED(rc)) throw MyError(rc, "failed to initialize COM!\n");
350
351 ComPtr<ISession> pSession;
352
353 RTPrintf("Creating VirtualBox object...\n");
354 rc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
355 if (SUCCEEDED(rc))
356 rc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
357 if (FAILED(rc)) throw MyError(rc, "failed to create the VirtualBox object!\n");
358
359 rc = pSession.createInprocObject(CLSID_Session);
360 if (FAILED(rc)) throw MyError(rc, "failed to create a session object!\n");
361
362 // for each testcase, we will copy the dummy VMDK image to the subdirectory with the OVF testcase
363 // so that the import will find the disks it expects; this is just for testing the import since
364 // the imported machines will obviously not be usable.
365 // llFiles2Delete receives the paths of all the files that we need to clean up later.
366
367 // testcase 1: import ovf-joomla-0.9/joomla-1.1.4-ovf.ovf
368 copyDummyDiskImage("joomla-0.9", llFiles2Delete, "ovf-testcases/ovf-joomla-0.9/joomla-1.1.4-ovf-0.vmdk");
369 copyDummyDiskImage("joomla-0.9", llFiles2Delete, "ovf-testcases/ovf-joomla-0.9/joomla-1.1.4-ovf-1.vmdk");
370 importOVF("joomla-0.9", pVirtualBox, "ovf-testcases/ovf-joomla-0.9/joomla-1.1.4-ovf.ovf", llMachinesCreated);
371
372 // testcase 2: import ovf-winxp-vbox-sharedfolders/winxp.ovf
373 copyDummyDiskImage("winxp-vbox-sharedfolders", llFiles2Delete, "ovf-testcases/ovf-winxp-vbox-sharedfolders/Windows 5.1 XP 1 merged.vmdk");
374 copyDummyDiskImage("winxp-vbox-sharedfolders", llFiles2Delete, "ovf-testcases/ovf-winxp-vbox-sharedfolders/smallvdi.vmdk");
375 importOVF("winxp-vbox-sharedfolders", pVirtualBox, "ovf-testcases/ovf-winxp-vbox-sharedfolders/winxp.ovf", llMachinesCreated);
376
377 // testcase 3: import ovf-winxp-vbox-sharedfolders/winxp.ovf
378 importOVF("winhost-audio-nodisks", pVirtualBox, "ovf-testcases/ovf-winhost-audio-nodisks/WinXP.ovf", llMachinesCreated);
379
380 RTPrintf("Machine imports done, no errors. Cleaning up...\n");
381 }
382 catch (MyError &e)
383 {
384 rc = e.m_rc;
385 RTPrintf("%s", e.m_str.c_str());
386 rcExit = RTEXITCODE_FAILURE;
387 }
388
389 try
390 {
391 // clean up the machines created
392 for (std::list<Guid>::const_iterator it = llMachinesCreated.begin();
393 it != llMachinesCreated.end();
394 ++it)
395 {
396 const Guid &uuid = *it;
397 Bstr bstrUUID(uuid.toUtf16());
398 ComPtr<IMachine> pMachine;
399 rc = pVirtualBox->FindMachine(bstrUUID.raw(), pMachine.asOutParam());
400 if (FAILED(rc)) throw MyError(rc, "VirtualBox::FindMachine() failed\n");
401
402 RTPrintf(" Deleting machine %ls...\n", bstrUUID.raw());
403 SafeIfaceArray<IMedium> sfaMedia;
404 rc = pMachine->Unregister(CleanupMode_DetachAllReturnHardDisksOnly,
405 ComSafeArrayAsOutParam(sfaMedia));
406 if (FAILED(rc)) throw MyError(rc, "Machine::Unregister() failed\n");
407
408 ComPtr<IProgress> pProgress;
409 rc = pMachine->DeleteConfig(ComSafeArrayAsInParam(sfaMedia), pProgress.asOutParam());
410 if (FAILED(rc)) throw MyError(rc, "Machine::DeleteSettings() failed\n");
411 rc = pProgress->WaitForCompletion(-1);
412 if (FAILED(rc)) throw MyError(rc, "Progress::WaitForCompletion() failed\n");
413 }
414 }
415 catch (MyError &e)
416 {
417 rc = e.m_rc;
418 RTPrintf("%s", e.m_str.c_str());
419 rcExit = RTEXITCODE_FAILURE;
420 }
421
422 // clean up the VMDK copies that we made in copyDummyDiskImage()
423 for (std::list<Utf8Str>::const_iterator it = llFiles2Delete.begin();
424 it != llFiles2Delete.end();
425 ++it)
426 {
427 const Utf8Str &strFile = *it;
428 RTPrintf("Deleting file %s...\n", strFile.c_str());
429 RTFileDelete(strFile.c_str());
430 }
431
432 pVirtualBox.setNull();
433 pVirtualBoxClient.setNull();
434
435 RTPrintf("Shutting down COM...\n");
436 com::Shutdown();
437 RTPrintf("tstOVF all done: %s\n", rcExit ? "ERROR" : "SUCCESS");
438
439 return rcExit;
440}
441
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use