VirtualBox

source: vbox/trunk/src/VBox/Main/ApplianceImpl.cpp@ 18672

Last change on this file since 18672 was 18627, checked in by vboxsync, 16 years ago

OVF: fix OVF 0.9 output to make ovftool happy

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 189.8 KB
Line 
1/* $Id: ApplianceImpl.cpp 18627 2009-04-02 11:15:36Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2008-2009 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <VBox/param.h>
24#include <iprt/stream.h>
25#include <iprt/path.h>
26#include <iprt/dir.h>
27#include <iprt/file.h>
28
29#include "ApplianceImpl.h"
30#include "VirtualBoxImpl.h"
31#include "GuestOSTypeImpl.h"
32#include "ProgressImpl.h"
33#include "MachineImpl.h"
34#include "HostNetworkInterfaceImpl.h"
35
36#include "Logging.h"
37
38#include "VBox/xml.h"
39
40using namespace std;
41
42////////////////////////////////////////////////////////////////////////////////
43//
44// hardware definitions
45//
46////////////////////////////////////////////////////////////////////////////////
47
48struct DiskImage
49{
50 Utf8Str strDiskId; // value from DiskSection/Disk/@diskId
51 int64_t iCapacity; // value from DiskSection/Disk/@capacity;
52 // (maximum size for dynamic images, I guess; we always translate this to bytes)
53 int64_t iPopulatedSize; // optional value from DiskSection/Disk/@populatedSize
54 // (actual used size of disk, always in bytes; can be an estimate of used disk
55 // space, but cannot be larger than iCapacity; -1 if not set)
56 Utf8Str strFormat; // value from DiskSection/Disk/@format
57 // typically http://www.vmware.com/specifications/vmdk.html#sparse
58
59 // fields from /References/File; the spec says the file reference from disk can be empty,
60 // so in that case, strFilename will be empty, then a new disk should be created
61 Utf8Str strHref; // value from /References/File/@href (filename); if empty, then the remaining fields are ignored
62 int64_t iSize; // value from /References/File/@size (optional according to spec; then we set -1 here)
63 int64_t iChunkSize; // value from /References/File/@chunkSize (optional, unsupported)
64 Utf8Str strCompression; // value from /References/File/@compression (optional, can be "gzip" according to spec)
65};
66
67struct VirtualHardwareItem
68{
69 Utf8Str strDescription;
70 Utf8Str strCaption;
71 Utf8Str strElementName;
72
73 uint32_t ulInstanceID;
74 uint32_t ulParent;
75
76 OVFResourceType_T resourceType;
77 Utf8Str strOtherResourceType;
78 Utf8Str strResourceSubType;
79
80 Utf8Str strHostResource; // "Abstractly specifies how a device shall connect to a resource on the deployment platform.
81 // Not all devices need a backing." Used with disk items, for which this references a virtual
82 // disk from the Disks section.
83 bool fAutomaticAllocation;
84 bool fAutomaticDeallocation;
85 Utf8Str strConnection; // "All Ethernet adapters that specify the same abstract network connection name within an OVF
86 // package shall be deployed on the same network. The abstract network connection name shall be
87 // listed in the NetworkSection at the outermost envelope level." We ignore this and only set up
88 // a network adapter depending on the network name.
89 Utf8Str strAddress; // "Device-specific. For an Ethernet adapter, this specifies the MAC address."
90 Utf8Str strAddressOnParent; // "For a device, this specifies its location on the controller."
91 Utf8Str strAllocationUnits; // "Specifies the units of allocation used. For example, “byte * 2^20”."
92 uint64_t ullVirtualQuantity; // "Specifies the quantity of resources presented. For example, “256”."
93 uint64_t ullReservation; // "Specifies the minimum quantity of resources guaranteed to be available."
94 uint64_t ullLimit; // "Specifies the maximum quantity of resources that will be granted."
95 uint64_t ullWeight; // "Specifies a relative priority for this allocation in relation to other allocations."
96
97 Utf8Str strConsumerVisibility;
98 Utf8Str strMappingBehavior;
99 Utf8Str strPoolID;
100 uint32_t ulBusNumber; // seen with IDE controllers, but not listed in OVF spec
101
102 uint32_t ulLineNumber; // line number of <Item> element in XML source; cached for error messages
103
104 VirtualHardwareItem()
105 : ulInstanceID(0), fAutomaticAllocation(false), fAutomaticDeallocation(false), ullVirtualQuantity(0), ullReservation(0), ullLimit(0), ullWeight(0), ulBusNumber(0), ulLineNumber(0)
106 {};
107};
108
109typedef map<Utf8Str, DiskImage> DiskImagesMap;
110
111struct VirtualSystem;
112
113typedef map<uint32_t, VirtualHardwareItem> HardwareItemsMap;
114
115struct HardDiskController
116{
117 uint32_t idController; // instance ID (Item/InstanceId); this gets referenced from HardDisk
118 enum ControllerSystemType { IDE, SATA, SCSI };
119 ControllerSystemType system; // one of IDE, SATA, SCSI
120 Utf8Str strControllerType; // controller subtype (Item/ResourceSubType); e.g. "LsiLogic"; can be empty (esp. for IDE)
121 Utf8Str strAddress; // for IDE
122 uint32_t ulBusNumber; // for IDE
123
124 HardDiskController()
125 : idController(0),
126 ulBusNumber(0)
127 {
128 }
129};
130
131typedef map<uint32_t, HardDiskController> ControllersMap;
132
133struct VirtualDisk
134{
135 uint32_t idController; // SCSI (or IDE) controller this disk is connected to;
136 // points into VirtualSystem.mapControllers
137 uint32_t ulAddressOnParent; // parsed strAddressOnParent of hardware item; will be 0 or 1 for IDE
138 // and possibly higher for disks attached to SCSI controllers (untested)
139 Utf8Str strDiskId; // if the hard disk has an ovf:/disk/<id> reference,
140 // this receives the <id> component; points to one of the
141 // references in Appliance::Data.mapDisks
142};
143
144typedef map<Utf8Str, VirtualDisk> VirtualDisksMap;
145
146struct EthernetAdapter
147{
148 Utf8Str strAdapterType; // "PCNet32" or "E1000" or whatever; from <rasd:ResourceSubType>
149 Utf8Str strNetworkName; // from <rasd:Connection>
150};
151
152typedef list<EthernetAdapter> EthernetAdaptersList;
153
154struct VirtualSystem
155{
156 Utf8Str strName; // copy of VirtualSystem/@id
157
158 Utf8Str strDescription; // copy of VirtualSystem/Info content
159
160 CIMOSType_T cimos;
161 Utf8Str strCimosDesc; // readable description of the cimos type in the case of cimos = 0/1/102
162 Utf8Str strVirtualSystemType; // generic hardware description; OVF says this can be something like "vmx-4" or "xen";
163 // VMware Workstation 6.5 is "vmx-07"
164
165 HardwareItemsMap mapHardwareItems; // map of virtual hardware items, sorted by unique instance ID
166
167 uint64_t ullMemorySize; // always in bytes, copied from llHardwareItems; default = 0 (unspecified)
168 uint16_t cCPUs; // no. of CPUs, copied from llHardwareItems; default = 1
169
170 EthernetAdaptersList llEthernetAdapters; // (one for each VirtualSystem/Item[@ResourceType=10]element)
171
172 ControllersMap mapControllers;
173 // list of hard disk controllers
174 // (one for each VirtualSystem/Item[@ResourceType=6] element with accumulated data from children)
175
176 VirtualDisksMap mapVirtualDisks;
177 // (one for each VirtualSystem/Item[@ResourceType=17] element with accumulated data from children)
178
179 bool fHasFloppyDrive; // true if there's a floppy item in mapHardwareItems
180 bool fHasCdromDrive; // true if there's a CD-ROM item in mapHardwareItems; ISO images are not yet supported by OVFtool
181 bool fHasUsbController; // true if there's a USB controller item in mapHardwareItems
182
183 Utf8Str strSoundCardType; // if not empty, then the system wants a soundcard; this then specifies the hardware;
184 // VMware Workstation 6.5 uses "ensoniq1371" for example
185
186 Utf8Str strLicenseText; // license info if any; receives contents of VirtualSystem/EulaSection/License
187
188 Utf8Str strProduct; // product info if any; receives contents of VirtualSystem/ProductSection/Product
189 Utf8Str strVendor; // product info if any; receives contents of VirtualSystem/ProductSection/Vendor
190 Utf8Str strVersion; // product info if any; receives contents of VirtualSystem/ProductSection/Version
191 Utf8Str strProductUrl; // product info if any; receives contents of VirtualSystem/ProductSection/ProductUrl
192 Utf8Str strVendorUrl; // product info if any; receives contents of VirtualSystem/ProductSection/VendorUrl
193
194 VirtualSystem()
195 : ullMemorySize(0), cCPUs(1), fHasFloppyDrive(false), fHasCdromDrive(false), fHasUsbController(false)
196 {
197 }
198};
199
200////////////////////////////////////////////////////////////////////////////////
201//
202// Appliance data definition
203//
204////////////////////////////////////////////////////////////////////////////////
205
206// opaque private instance data of Appliance class
207struct Appliance::Data
208{
209 Utf8Str strPath; // file name last given to either read() or write()
210
211 DiskImagesMap mapDisks; // map of DiskImage structs, sorted by DiskImage.strDiskId
212
213 list<VirtualSystem> llVirtualSystems; // list of virtual systems, created by and valid after read()
214
215 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions; //
216
217 list<Utf8Str> llWarnings;
218
219 ULONG ulWeightPerOperation; // for progress calculations
220};
221
222struct VirtualSystemDescription::Data
223{
224 list<VirtualSystemDescriptionEntry> llDescriptions;
225};
226
227////////////////////////////////////////////////////////////////////////////////
228//
229// internal helpers
230//
231////////////////////////////////////////////////////////////////////////////////
232
233static Utf8Str stripFilename(const Utf8Str &strFile)
234{
235 Utf8Str str2(strFile);
236 RTPathStripFilename(str2.mutableRaw());
237 return str2;
238}
239
240static const struct
241{
242 CIMOSType_T cim;
243 const char *pcszVbox;
244}
245 g_osTypes[] =
246 {
247 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
248 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
249 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
250 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
251 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
252 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
253 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
254 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
255 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
256 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_OpenSolaris },
257 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_OpenSolaris },
258 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
259 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
260 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
261 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
262 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
263 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
264 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
265 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
266 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
267 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
268 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
269 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
270 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
271 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
272 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
273 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
274 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
275 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
276 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
277 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_OpenSolaris_64 },
278 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
279 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
280 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
281 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
282 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
283 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
284 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
285 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},
286
287 // { CIMOSType_CIMOS_TurboLinux_64, },
288
289 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
290 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
291 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
292 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
293 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
294 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
295 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
296 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
297 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
298 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 },
299 { CIMOSType_CIMOS_Linux_64, SchemaDefs_OSTypeId_Linux26_64 }
300};
301
302/* Pattern structure for matching the os type description field */
303struct osTypePattern
304{
305 const char *pcszPattern;
306 const char *pcszVbox;
307};
308
309/* These are the 32-Bit ones. They are sorted by priority. */
310static const osTypePattern g_osTypesPattern[] =
311{
312 {"Windows NT", SchemaDefs_OSTypeId_WindowsNT4},
313 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP},
314 {"Windows 2000", SchemaDefs_OSTypeId_Windows2000},
315 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003},
316 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista},
317 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008},
318 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE},
319 {"Novell", SchemaDefs_OSTypeId_OpenSUSE},
320 {"Red Hat", SchemaDefs_OSTypeId_RedHat},
321 {"Mandriva", SchemaDefs_OSTypeId_Mandriva},
322 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu},
323 {"Debian", SchemaDefs_OSTypeId_Debian},
324 {"QNX", SchemaDefs_OSTypeId_QNX},
325 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24},
326 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26},
327 {"Linux", SchemaDefs_OSTypeId_Linux},
328 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris},
329 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris},
330 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD},
331 {"NetBSD", SchemaDefs_OSTypeId_NetBSD},
332 {"Windows 95", SchemaDefs_OSTypeId_Windows95},
333 {"Windows 98", SchemaDefs_OSTypeId_Windows98},
334 {"Windows Me", SchemaDefs_OSTypeId_WindowsMe},
335 {"Windows 3.", SchemaDefs_OSTypeId_Windows31},
336 {"DOS", SchemaDefs_OSTypeId_DOS},
337 {"OS2", SchemaDefs_OSTypeId_OS2}
338};
339
340/* These are the 64-Bit ones. They are sorted by priority. */
341static const osTypePattern g_osTypesPattern64[] =
342{
343 {"Windows XP", SchemaDefs_OSTypeId_WindowsXP_64},
344 {"Windows 2003", SchemaDefs_OSTypeId_Windows2003_64},
345 {"Windows Vista", SchemaDefs_OSTypeId_WindowsVista_64},
346 {"Windows 2008", SchemaDefs_OSTypeId_Windows2008_64},
347 {"SUSE", SchemaDefs_OSTypeId_OpenSUSE_64},
348 {"Novell", SchemaDefs_OSTypeId_OpenSUSE_64},
349 {"Red Hat", SchemaDefs_OSTypeId_RedHat_64},
350 {"Mandriva", SchemaDefs_OSTypeId_Mandriva_64},
351 {"Ubuntu", SchemaDefs_OSTypeId_Ubuntu_64},
352 {"Debian", SchemaDefs_OSTypeId_Debian_64},
353 {"Linux 2.4", SchemaDefs_OSTypeId_Linux24_64},
354 {"Linux 2.6", SchemaDefs_OSTypeId_Linux26_64},
355 {"Linux", SchemaDefs_OSTypeId_Linux26_64},
356 {"OpenSolaris", SchemaDefs_OSTypeId_OpenSolaris_64},
357 {"Solaris", SchemaDefs_OSTypeId_OpenSolaris_64},
358 {"FreeBSD", SchemaDefs_OSTypeId_FreeBSD_64},
359};
360
361/**
362 * Private helper func that suggests a VirtualBox guest OS type
363 * for the given OVF operating system type.
364 * @param osTypeVBox
365 * @param c
366 * @param cStr
367 */
368static void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c, const Utf8Str &cStr)
369{
370 /* First check if the type is other/other_64 */
371 if (c == CIMOSType_CIMOS_Other)
372 {
373 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern); ++i)
374 if (cStr.contains (g_osTypesPattern[i].pcszPattern, Utf8Str::CaseInsensitive))
375 {
376 strType = g_osTypesPattern[i].pcszVbox;
377 return;
378 }
379 }
380 else if (c == CIMOSType_CIMOS_Other_64)
381 {
382 for (size_t i=0; i < RT_ELEMENTS(g_osTypesPattern64); ++i)
383 if (cStr.contains (g_osTypesPattern64[i].pcszPattern, Utf8Str::CaseInsensitive))
384 {
385 strType = g_osTypesPattern64[i].pcszVbox;
386 return;
387 }
388 }
389
390 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
391 {
392 if (c == g_osTypes[i].cim)
393 {
394 strType = g_osTypes[i].pcszVbox;
395 return;
396 }
397 }
398
399 strType = SchemaDefs_OSTypeId_Other;
400}
401
402/**
403 * Private helper func that suggests a VirtualBox guest OS type
404 * for the given OVF operating system type.
405 * @param osTypeVBox
406 * @param c
407 */
408static CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)
409{
410 for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i)
411 {
412 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))
413 return g_osTypes[i].cim;
414 }
415
416 return CIMOSType_CIMOS_Other;
417}
418
419////////////////////////////////////////////////////////////////////////////////
420//
421// IVirtualBox public methods
422//
423////////////////////////////////////////////////////////////////////////////////
424
425// This code is here so we won't have to include the appliance headers in the
426// IVirtualBox implementation.
427
428/**
429 * Implementation for IVirtualBox::createAppliance.
430 *
431 * @param anAppliance IAppliance object created if S_OK is returned.
432 * @return S_OK or error.
433 */
434STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
435{
436 HRESULT rc;
437
438 ComObjPtr<Appliance> appliance;
439 appliance.createObject();
440 rc = appliance->init(this);
441
442 if (SUCCEEDED(rc))
443 appliance.queryInterfaceTo(anAppliance);
444
445 return rc;
446}
447
448////////////////////////////////////////////////////////////////////////////////
449//
450// Appliance constructor / destructor
451//
452////////////////////////////////////////////////////////////////////////////////
453
454DEFINE_EMPTY_CTOR_DTOR(Appliance)
455struct shutup {};
456
457/**
458 * Appliance COM initializer.
459 * @param
460 * @return
461 */
462
463HRESULT Appliance::init(VirtualBox *aVirtualBox)
464{
465 /* Enclose the state transition NotReady->InInit->Ready */
466 AutoInitSpan autoInitSpan(this);
467 AssertReturn(autoInitSpan.isOk(), E_FAIL);
468
469 /* Weak reference to a VirtualBox object */
470 unconst(mVirtualBox) = aVirtualBox;
471
472 // initialize data
473 m = new Data;
474
475 /* Confirm a successful initialization */
476 autoInitSpan.setSucceeded();
477
478 return S_OK;
479}
480
481/**
482 * Appliance COM uninitializer.
483 * @return
484 */
485void Appliance::uninit()
486{
487 delete m;
488 m = NULL;
489}
490
491////////////////////////////////////////////////////////////////////////////////
492//
493// Appliance private methods
494//
495////////////////////////////////////////////////////////////////////////////////
496
497/**
498 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
499 * and handles the contained child elements (which can be "Section" or "Content" elements).
500 *
501 * @param pcszPath Path spec of the XML file, for error messages.
502 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
503 * @param pCurElem Element whose children are to be analyzed here.
504 * @return
505 */
506HRESULT Appliance::LoopThruSections(const char *pcszPath,
507 const xml::ElementNode *pReferencesElem,
508 const xml::ElementNode *pCurElem)
509{
510 HRESULT rc;
511
512 xml::NodesLoop loopChildren(*pCurElem);
513 const xml::ElementNode *pElem;
514 while ((pElem = loopChildren.forAllNodes()))
515 {
516 const char *pcszElemName = pElem->getName();
517 const char *pcszTypeAttr = "";
518 const xml::AttributeNode *pTypeAttr;
519 if ((pTypeAttr = pElem->findAttribute("type")))
520 pcszTypeAttr = pTypeAttr->getValue();
521
522 if ( (!strcmp(pcszElemName, "DiskSection"))
523 || ( (!strcmp(pcszElemName, "Section"))
524 && (!strcmp(pcszTypeAttr, "ovf:DiskSection_Type"))
525 )
526 )
527 {
528 if (!(SUCCEEDED((rc = HandleDiskSection(pcszPath, pReferencesElem, pElem)))))
529 return rc;
530 }
531 else if ( (!strcmp(pcszElemName, "NetworkSection"))
532 || ( (!strcmp(pcszElemName, "Section"))
533 && (!strcmp(pcszTypeAttr, "ovf:NetworkSection_Type"))
534 )
535 )
536 {
537 if (!(SUCCEEDED((rc = HandleNetworkSection(pcszPath, pElem)))))
538 return rc;
539 }
540 else if ( (!strcmp(pcszElemName, "DeploymentOptionSection")))
541 {
542 // TODO
543 }
544 else if ( (!strcmp(pcszElemName, "Info")))
545 {
546 // child of VirtualSystemCollection -- TODO
547 }
548 else if ( (!strcmp(pcszElemName, "ResourceAllocationSection")))
549 {
550 // child of VirtualSystemCollection -- TODO
551 }
552 else if ( (!strcmp(pcszElemName, "StartupSection")))
553 {
554 // child of VirtualSystemCollection -- TODO
555 }
556 else if ( (!strcmp(pcszElemName, "VirtualSystem"))
557 || ( (!strcmp(pcszElemName, "Content"))
558 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type"))
559 )
560 )
561 {
562 if (!(SUCCEEDED((rc = HandleVirtualSystemContent(pcszPath, pElem)))))
563 return rc;
564 }
565 else if ( (!strcmp(pcszElemName, "VirtualSystemCollection"))
566 || ( (!strcmp(pcszElemName, "Content"))
567 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type"))
568 )
569 )
570 {
571 // TODO ResourceAllocationSection
572
573 // recurse for this, since it has VirtualSystem elements as children
574 if (!(SUCCEEDED((rc = LoopThruSections(pcszPath, pReferencesElem, pElem)))))
575 return rc;
576 }
577 }
578
579 return S_OK;
580}
581
582/**
583 * Private helper method that handles disk sections in the OVF XML.
584 * Gets called indirectly from IAppliance::read().
585 *
586 * @param pcszPath Path spec of the XML file, for error messages.
587 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
588 * @param pSectionElem Section element for which this helper is getting called.
589 * @return
590 */
591HRESULT Appliance::HandleDiskSection(const char *pcszPath,
592 const xml::ElementNode *pReferencesElem,
593 const xml::ElementNode *pSectionElem)
594{
595 // contains "Disk" child elements
596 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
597 const xml::ElementNode *pelmDisk;
598 while ((pelmDisk = loopDisks.forAllNodes()))
599 {
600 DiskImage d;
601 const char *pcszBad = NULL;
602 if (!(pelmDisk->getAttributeValue("diskId", d.strDiskId)))
603 pcszBad = "diskId";
604 else if (!(pelmDisk->getAttributeValue("format", d.strFormat)))
605 pcszBad = "format";
606 else if (!(pelmDisk->getAttributeValue("capacity", d.iCapacity)))
607 pcszBad = "capacity";
608 else
609 {
610 if (!(pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)))
611 // optional
612 d.iPopulatedSize = -1;
613
614 Utf8Str strFileRef;
615 if (pelmDisk->getAttributeValue("fileRef", strFileRef)) // optional
616 {
617 // look up corresponding /References/File nodes (list built above)
618 const xml::ElementNode *pFileElem;
619 if ( pReferencesElem
620 && ((pFileElem = pReferencesElem->findChildElementFromId(strFileRef.c_str())))
621 )
622 {
623 // copy remaining values from file node then
624 const char *pcszBadInFile = NULL;
625 if (!(pFileElem->getAttributeValue("href", d.strHref)))
626 pcszBadInFile = "href";
627 else if (!(pFileElem->getAttributeValue("size", d.iSize)))
628 d.iSize = -1; // optional
629 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
630 d.iChunkSize = -1; // optional
631 pFileElem->getAttributeValue("compression", d.strCompression);
632
633 if (pcszBadInFile)
634 return setError(VBOX_E_FILE_ERROR,
635 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
636 pcszPath,
637 pcszBadInFile,
638 pFileElem->getLineNumber());
639 }
640 else
641 return setError(VBOX_E_FILE_ERROR,
642 tr("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
643 pcszPath,
644 strFileRef.c_str(),
645 pelmDisk->getLineNumber());
646 }
647 }
648
649 if (pcszBad)
650 return setError(VBOX_E_FILE_ERROR,
651 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
652 pcszPath,
653 pcszBad,
654 pelmDisk->getLineNumber());
655
656 m->mapDisks[d.strDiskId] = d;
657 }
658
659 return S_OK;
660}
661
662/**
663 * Private helper method that handles network sections in the OVF XML.
664 * Gets called indirectly from IAppliance::read().
665 *
666 * @param pcszPath Path spec of the XML file, for error messages.
667 * @param pSectionElem Section element for which this helper is getting called.
668 * @return
669 */
670HRESULT Appliance::HandleNetworkSection(const char * /* pcszPath */,
671 const xml::ElementNode * /* pSectionElem */)
672{
673 // we ignore network sections for now
674
675// xml::NodesLoop loopNetworks(*pSectionElem, "Network");
676// const xml::Node *pelmNetwork;
677// while ((pelmNetwork = loopNetworks.forAllNodes()))
678// {
679// Network n;
680// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
681// return setError(VBOX_E_FILE_ERROR,
682// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
683// pcszPath,
684// pelmNetwork->getLineNumber());
685//
686// m->mapNetworks[n.strNetworkName] = n;
687// }
688
689 return S_OK;
690}
691
692/**
693 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
694 * Gets called indirectly from IAppliance::read().
695 *
696 * @param pcszPath
697 * @param pContentElem
698 * @return
699 */
700HRESULT Appliance::HandleVirtualSystemContent(const char *pcszPath,
701 const xml::ElementNode *pelmVirtualSystem)
702{
703 VirtualSystem vsys;
704
705 const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
706 if (pIdAttr)
707 vsys.strName = pIdAttr->getValue();
708
709 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
710 const xml::ElementNode *pelmThis;
711 while ((pelmThis = loop.forAllNodes()))
712 {
713 const char *pcszElemName = pelmThis->getName();
714 const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
715 const char *pcszTypeAttr = (pTypeAttr) ? pTypeAttr->getValue() : "";
716
717 if ( (!strcmp(pcszElemName, "EulaSection"))
718 || (!strcmp(pcszTypeAttr, "ovf:EulaSection_Type"))
719 )
720 {
721 /* <EulaSection>
722 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
723 <License ovf:msgid="1">License terms can go in here.</License>
724 </EulaSection> */
725
726 const xml::ElementNode *pelmLicense;
727 if ((pelmLicense = pelmThis->findChildElement("License")))
728 vsys.strLicenseText = pelmLicense->getValue();
729 }
730 if ( (!strcmp(pcszElemName, "ProductSection"))
731 || (!strcmp(pcszTypeAttr, "ovf:ProductSection_Type"))
732 )
733 {
734 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
735 <Info>Meta-information about the installed software</Info>
736 <Product>VAtest</Product>
737 <Vendor>SUN Microsystems</Vendor>
738 <Version>10.0</Version>
739 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
740 <VendorUrl>http://www.sun.com</VendorUrl>
741 </Section> */
742 const xml::ElementNode *pelmProduct;
743 if ((pelmProduct = pelmThis->findChildElement("Product")))
744 vsys.strProduct = pelmProduct->getValue();
745 const xml::ElementNode *pelmVendor;
746 if ((pelmVendor = pelmThis->findChildElement("Vendor")))
747 vsys.strVendor = pelmVendor->getValue();
748 const xml::ElementNode *pelmVersion;
749 if ((pelmVersion = pelmThis->findChildElement("Version")))
750 vsys.strVersion = pelmVersion->getValue();
751 const xml::ElementNode *pelmProductUrl;
752 if ((pelmProductUrl = pelmThis->findChildElement("ProductUrl")))
753 vsys.strProductUrl = pelmProductUrl->getValue();
754 const xml::ElementNode *pelmVendorUrl;
755 if ((pelmVendorUrl = pelmThis->findChildElement("VendorUrl")))
756 vsys.strVendorUrl = pelmVendorUrl->getValue();
757 }
758 else if ( (!strcmp(pcszElemName, "VirtualHardwareSection"))
759 || (!strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type"))
760 )
761 {
762 const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
763 if ((pelmSystem = pelmThis->findChildElement("System")))
764 {
765 /* <System>
766 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
767 <vssd:ElementName>vmware</vssd:ElementName>
768 <vssd:InstanceID>1</vssd:InstanceID>
769 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
770 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
771 </System>*/
772 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
773 vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
774 }
775
776 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
777 const xml::ElementNode *pelmItem;
778 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
779 {
780 VirtualHardwareItem i;
781
782 i.ulLineNumber = pelmItem->getLineNumber();
783
784 xml::NodesLoop loopItemChildren(*pelmItem); // all child elements
785 const xml::ElementNode *pelmItemChild;
786 while ((pelmItemChild = loopItemChildren.forAllNodes()))
787 {
788 const char *pcszItemChildName = pelmItemChild->getName();
789 if (!strcmp(pcszItemChildName, "Description"))
790 i.strDescription = pelmItemChild->getValue();
791 else if (!strcmp(pcszItemChildName, "Caption"))
792 i.strCaption = pelmItemChild->getValue();
793 else if (!strcmp(pcszItemChildName, "ElementName"))
794 i.strElementName = pelmItemChild->getValue();
795 else if ( (!strcmp(pcszItemChildName, "InstanceID"))
796 || (!strcmp(pcszItemChildName, "InstanceId"))
797 )
798 pelmItemChild->copyValue(i.ulInstanceID);
799 else if (!strcmp(pcszItemChildName, "HostResource"))
800 i.strHostResource = pelmItemChild->getValue();
801 else if (!strcmp(pcszItemChildName, "ResourceType"))
802 {
803 uint32_t ulType;
804 pelmItemChild->copyValue(ulType);
805 i.resourceType = (OVFResourceType_T)ulType;
806 }
807 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
808 i.strOtherResourceType = pelmItemChild->getValue();
809 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
810 i.strResourceSubType = pelmItemChild->getValue();
811 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
812 i.fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
813 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
814 i.fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
815 else if (!strcmp(pcszItemChildName, "Parent"))
816 pelmItemChild->copyValue(i.ulParent);
817 else if (!strcmp(pcszItemChildName, "Connection"))
818 i.strConnection = pelmItemChild->getValue();
819 else if (!strcmp(pcszItemChildName, "Address"))
820 i.strAddress = pelmItemChild->getValue();
821 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
822 i.strAddressOnParent = pelmItemChild->getValue();
823 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
824 i.strAllocationUnits = pelmItemChild->getValue();
825 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
826 pelmItemChild->copyValue(i.ullVirtualQuantity);
827 else if (!strcmp(pcszItemChildName, "Reservation"))
828 pelmItemChild->copyValue(i.ullReservation);
829 else if (!strcmp(pcszItemChildName, "Limit"))
830 pelmItemChild->copyValue(i.ullLimit);
831 else if (!strcmp(pcszItemChildName, "Weight"))
832 pelmItemChild->copyValue(i.ullWeight);
833 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
834 i.strConsumerVisibility = pelmItemChild->getValue();
835 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
836 i.strMappingBehavior = pelmItemChild->getValue();
837 else if (!strcmp(pcszItemChildName, "PoolID"))
838 i.strPoolID = pelmItemChild->getValue();
839 else if (!strcmp(pcszItemChildName, "BusNumber"))
840 pelmItemChild->copyValue(i.ulBusNumber);
841 else
842 return setError(VBOX_E_FILE_ERROR,
843 tr("Error reading \"%s\": unknown element \"%s\" under Item element, line %d"),
844 pcszPath,
845 pcszItemChildName,
846 i.ulLineNumber);
847 }
848
849 // store!
850 vsys.mapHardwareItems[i.ulInstanceID] = i;
851 }
852
853 // now go thru all hardware items and handle them according to their type;
854 // in this first loop we handle all items _except_ hard disk images,
855 // which we'll handle in a second loop below
856 HardwareItemsMap::const_iterator itH;
857 for (itH = vsys.mapHardwareItems.begin();
858 itH != vsys.mapHardwareItems.end();
859 ++itH)
860 {
861 const VirtualHardwareItem &i = itH->second;
862
863 // do some analysis
864 switch (i.resourceType)
865 {
866 case OVFResourceType_Processor: // 3
867 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
868 <rasd:Description>Number of virtual CPUs</rasd:Description>
869 <rasd:ElementName>virtual CPU</rasd:ElementName>
870 <rasd:InstanceID>1</rasd:InstanceID>
871 <rasd:ResourceType>3</rasd:ResourceType>
872 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
873 if (i.ullVirtualQuantity < UINT16_MAX)
874 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
875 else
876 return setError(VBOX_E_FILE_ERROR,
877 tr("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
878 pcszPath,
879 i.ullVirtualQuantity,
880 UINT16_MAX,
881 i.ulLineNumber);
882 break;
883
884 case OVFResourceType_Memory: // 4
885 if ( (i.strAllocationUnits == "MegaBytes") // found in OVF created by OVF toolkit
886 || (i.strAllocationUnits == "MB") // found in MS docs
887 || (i.strAllocationUnits == "byte * 2^20") // suggested by OVF spec DSP0243 page 21
888 )
889 vsys.ullMemorySize = i.ullVirtualQuantity * 1024 * 1024;
890 else
891 return setError(VBOX_E_FILE_ERROR,
892 tr("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
893 pcszPath,
894 i.strAllocationUnits.c_str(),
895 i.ulLineNumber);
896 break;
897
898 case OVFResourceType_IDEController: // 5
899 {
900 /* <Item>
901 <rasd:Caption>ideController0</rasd:Caption>
902 <rasd:Description>IDE Controller</rasd:Description>
903 <rasd:InstanceId>5</rasd:InstanceId>
904 <rasd:ResourceType>5</rasd:ResourceType>
905 <rasd:Address>0</rasd:Address>
906 <rasd:BusNumber>0</rasd:BusNumber>
907 </Item> */
908 HardDiskController hdc;
909 hdc.system = HardDiskController::IDE;
910 hdc.idController = i.ulInstanceID;
911 hdc.strControllerType = i.strResourceSubType;
912 hdc.strAddress = i.strAddress;
913 hdc.ulBusNumber = i.ulBusNumber;
914
915 vsys.mapControllers[i.ulInstanceID] = hdc;
916 }
917 break;
918
919 case OVFResourceType_ParallelSCSIHBA: // 6 SCSI controller
920 {
921 /* <Item>
922 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
923 <rasd:Description>SCI Controller</rasd:Description>
924 <rasd:ElementName>SCSI controller</rasd:ElementName>
925 <rasd:InstanceID>4</rasd:InstanceID>
926 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
927 <rasd:ResourceType>6</rasd:ResourceType>
928 </Item> */
929 HardDiskController hdc;
930 hdc.system = HardDiskController::SCSI;
931 hdc.idController = i.ulInstanceID;
932 hdc.strControllerType = i.strResourceSubType;
933
934 vsys.mapControllers[i.ulInstanceID] = hdc;
935 }
936 break;
937
938 case OVFResourceType_EthernetAdapter: // 10
939 {
940 /* <Item>
941 <rasd:Caption>Ethernet adapter on 'Bridged'</rasd:Caption>
942 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
943 <rasd:Connection>Bridged</rasd:Connection>
944 <rasd:InstanceID>6</rasd:InstanceID>
945 <rasd:ResourceType>10</rasd:ResourceType>
946 <rasd:ResourceSubType>E1000</rasd:ResourceSubType>
947 </Item>
948
949 OVF spec DSP 0243 page 21:
950 "For an Ethernet adapter, this specifies the abstract network connection name
951 for the virtual machine. All Ethernet adapters that specify the same abstract
952 network connection name within an OVF package shall be deployed on the same
953 network. The abstract network connection name shall be listed in the NetworkSection
954 at the outermost envelope level." */
955
956 // only store the name
957 EthernetAdapter ea;
958 ea.strAdapterType = i.strResourceSubType;
959 ea.strNetworkName = i.strConnection;
960 vsys.llEthernetAdapters.push_back(ea);
961 }
962 break;
963
964 case OVFResourceType_FloppyDrive: // 14
965 vsys.fHasFloppyDrive = true; // we have no additional information
966 break;
967
968 case OVFResourceType_CDDrive: // 15
969 /* <Item ovf:required="false">
970 <rasd:Caption>cdrom1</rasd:Caption>
971 <rasd:InstanceId>7</rasd:InstanceId>
972 <rasd:ResourceType>15</rasd:ResourceType>
973 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
974 <rasd:Parent>5</rasd:Parent>
975 <rasd:AddressOnParent>0</rasd:AddressOnParent>
976 </Item> */
977 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
978 // but then the ovftool dies with "Device backing not supported". So I guess if
979 // VMware can't export ISOs, then we don't need to be able to import them right now.
980 vsys.fHasCdromDrive = true; // we have no additional information
981 break;
982
983 case OVFResourceType_HardDisk: // 17
984 // handled separately in second loop below
985 break;
986
987 case OVFResourceType_OtherStorageDevice: // 20 SATA controller
988 {
989 /* <Item>
990 <rasd:Description>SATA Controller</rasd:Description>
991 <rasd:Caption>sataController0</rasd:Caption>
992 <rasd:InstanceID>4</rasd:InstanceID>
993 <rasd:ResourceType>20</rasd:ResourceType>
994 <rasd:ResourceSubType>AHCI</rasd:ResourceSubType>
995 <rasd:Address>0</rasd:Address>
996 <rasd:BusNumber>0</rasd:BusNumber>
997 </Item> */
998 if (i.strCaption.startsWith ("sataController", Utf8Str::CaseInsensitive) &&
999 !i.strResourceSubType.compare ("AHCI", Utf8Str::CaseInsensitive))
1000 {
1001 HardDiskController hdc;
1002 hdc.system = HardDiskController::SATA;
1003 hdc.idController = i.ulInstanceID;
1004 hdc.strControllerType = i.strResourceSubType;
1005
1006 vsys.mapControllers[i.ulInstanceID] = hdc;
1007 }
1008 else
1009 return setError(VBOX_E_FILE_ERROR,
1010 tr("Error reading \"%s\": Host resource of type \"Other Storage Device (%d)\" is supported with SATA AHCI controllers only, line %d"),
1011 pcszPath,
1012 OVFResourceType_OtherStorageDevice,
1013 i.ulLineNumber);
1014 }
1015 break;
1016
1017 case OVFResourceType_USBController: // 23
1018 /* <Item ovf:required="false">
1019 <rasd:Caption>usb</rasd:Caption>
1020 <rasd:Description>USB Controller</rasd:Description>
1021 <rasd:InstanceId>3</rasd:InstanceId>
1022 <rasd:ResourceType>23</rasd:ResourceType>
1023 <rasd:Address>0</rasd:Address>
1024 <rasd:BusNumber>0</rasd:BusNumber>
1025 </Item> */
1026 vsys.fHasUsbController = true; // we have no additional information
1027 break;
1028
1029 case OVFResourceType_SoundCard: // 35
1030 /* <Item ovf:required="false">
1031 <rasd:Caption>sound</rasd:Caption>
1032 <rasd:Description>Sound Card</rasd:Description>
1033 <rasd:InstanceId>10</rasd:InstanceId>
1034 <rasd:ResourceType>35</rasd:ResourceType>
1035 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
1036 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
1037 <rasd:AddressOnParent>3</rasd:AddressOnParent>
1038 </Item> */
1039 vsys.strSoundCardType = i.strResourceSubType;
1040 break;
1041
1042 default:
1043 return setError(VBOX_E_FILE_ERROR,
1044 tr("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
1045 pcszPath,
1046 i.resourceType,
1047 i.ulLineNumber);
1048 } // end switch
1049 }
1050
1051 // now run through the items for a second time, but handle only
1052 // hard disk images; otherwise the code would fail if a hard
1053 // disk image appears in the OVF before its hard disk controller
1054 for (itH = vsys.mapHardwareItems.begin();
1055 itH != vsys.mapHardwareItems.end();
1056 ++itH)
1057 {
1058 const VirtualHardwareItem &i = itH->second;
1059
1060 // do some analysis
1061 switch (i.resourceType)
1062 {
1063 case OVFResourceType_HardDisk: // 17
1064 {
1065 /* <Item>
1066 <rasd:Caption>Harddisk 1</rasd:Caption>
1067 <rasd:Description>HD</rasd:Description>
1068 <rasd:ElementName>Hard Disk</rasd:ElementName>
1069 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
1070 <rasd:InstanceID>5</rasd:InstanceID>
1071 <rasd:Parent>4</rasd:Parent>
1072 <rasd:ResourceType>17</rasd:ResourceType>
1073 </Item> */
1074
1075 // look up the hard disk controller element whose InstanceID equals our Parent;
1076 // this is how the connection is specified in OVF
1077 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
1078 if (it == vsys.mapControllers.end())
1079 return setError(VBOX_E_FILE_ERROR,
1080 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid parent %d, line %d"),
1081 pcszPath,
1082 i.ulInstanceID,
1083 i.ulParent,
1084 i.ulLineNumber);
1085 //const HardDiskController &hdc = it->second;
1086
1087 VirtualDisk vd;
1088 vd.idController = i.ulParent;
1089 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
1090 // ovf://disk/lamp
1091 // 123456789012345
1092 if (i.strHostResource.substr(0, 11) == "ovf://disk/")
1093 vd.strDiskId = i.strHostResource.substr(11);
1094 else if (i.strHostResource.substr(0, 6) == "/disk/")
1095 vd.strDiskId = i.strHostResource.substr(6);
1096
1097 if ( !(vd.strDiskId.length())
1098 || (m->mapDisks.find(vd.strDiskId) == m->mapDisks.end())
1099 )
1100 return setError(VBOX_E_FILE_ERROR,
1101 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
1102 pcszPath,
1103 i.ulInstanceID,
1104 i.strHostResource.c_str(),
1105 i.ulLineNumber);
1106
1107 vsys.mapVirtualDisks[vd.strDiskId] = vd;
1108 }
1109 break;
1110 }
1111 }
1112 }
1113 else if ( (!strcmp(pcszElemName, "OperatingSystemSection"))
1114 || (!strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type"))
1115 )
1116 {
1117 uint64_t cimos64;
1118 if (!(pelmThis->getAttributeValue("id", cimos64)))
1119 return setError(VBOX_E_FILE_ERROR,
1120 tr("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
1121 pcszPath,
1122 pelmThis->getLineNumber());
1123
1124 vsys.cimos = (CIMOSType_T)cimos64;
1125 const xml::ElementNode *pelmCIMOSDescription;
1126 if ((pelmCIMOSDescription = pelmThis->findChildElement("Description")))
1127 vsys.strCimosDesc = pelmCIMOSDescription->getValue();
1128 }
1129 else if ( (!strcmp(pcszElemName, "AnnotationSection"))
1130 || (!strcmp(pcszTypeAttr, "ovf:AnnotationSection_Type"))
1131 )
1132 {
1133 const xml::ElementNode *pelmAnnotation;
1134 if ((pelmAnnotation = pelmThis->findChildElement("Annotation")))
1135 vsys.strDescription = pelmAnnotation->getValue();
1136 }
1137 }
1138
1139 // now create the virtual system
1140 m->llVirtualSystems.push_back(vsys);
1141
1142 return S_OK;
1143}
1144
1145////////////////////////////////////////////////////////////////////////////////
1146//
1147// IAppliance public methods
1148//
1149////////////////////////////////////////////////////////////////////////////////
1150
1151/**
1152 * Public method implementation.
1153 * @param
1154 * @return
1155 */
1156STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
1157{
1158 if (!aPath)
1159 return E_POINTER;
1160
1161 AutoCaller autoCaller(this);
1162 CheckComRCReturnRC(autoCaller.rc());
1163
1164 AutoReadLock alock(this);
1165
1166 Bstr bstrPath(m->strPath);
1167 bstrPath.cloneTo(aPath);
1168
1169 return S_OK;
1170}
1171
1172/**
1173 * Public method implementation.
1174 * @param
1175 * @return
1176 */
1177STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
1178{
1179 CheckComArgOutSafeArrayPointerValid(aDisks);
1180
1181 AutoCaller autoCaller(this);
1182 CheckComRCReturnRC(autoCaller.rc());
1183
1184 AutoReadLock alock(this);
1185
1186 size_t c = m->mapDisks.size();
1187 com::SafeArray<BSTR> sfaDisks(c);
1188
1189 DiskImagesMap::const_iterator it;
1190 size_t i = 0;
1191 for (it = m->mapDisks.begin();
1192 it != m->mapDisks.end();
1193 ++it, ++i)
1194 {
1195 // create a string representing this disk
1196 const DiskImage &d = it->second;
1197 char *psz = NULL;
1198 RTStrAPrintf(&psz,
1199 "%s\t"
1200 "%RI64\t"
1201 "%RI64\t"
1202 "%s\t"
1203 "%s\t"
1204 "%RI64\t"
1205 "%RI64\t"
1206 "%s",
1207 d.strDiskId.c_str(),
1208 d.iCapacity,
1209 d.iPopulatedSize,
1210 d.strFormat.c_str(),
1211 d.strHref.c_str(),
1212 d.iSize,
1213 d.iChunkSize,
1214 d.strCompression.c_str());
1215 Utf8Str utf(psz);
1216 Bstr bstr(utf);
1217 // push to safearray
1218 bstr.cloneTo(&sfaDisks[i]);
1219 RTStrFree(psz);
1220 }
1221
1222 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
1223
1224 return S_OK;
1225}
1226
1227/**
1228 * Public method implementation.
1229 * @param
1230 * @return
1231 */
1232STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
1233{
1234 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
1235
1236 AutoCaller autoCaller(this);
1237 CheckComRCReturnRC(autoCaller.rc());
1238
1239 AutoReadLock alock(this);
1240
1241 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
1242 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
1243
1244 return S_OK;
1245}
1246
1247/**
1248 * Public method implementation.
1249 * @param path
1250 * @return
1251 */
1252STDMETHODIMP Appliance::Read(IN_BSTR path)
1253{
1254 HRESULT rc = S_OK;
1255
1256 if (!path)
1257 return E_POINTER;
1258
1259 AutoCaller autoCaller(this);
1260 CheckComRCReturnRC(autoCaller.rc());
1261
1262 AutoWriteLock alock(this);
1263
1264 // see if we can handle this file; for now we insist it has an ".ovf" extension
1265 m->strPath = path;
1266 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
1267 return setError(VBOX_E_FILE_ERROR,
1268 tr("Appliance file must have .ovf extension"));
1269
1270 try
1271 {
1272 xml::XmlFileParser parser;
1273 xml::Document doc;
1274 parser.read(m->strPath.raw(),
1275 doc);
1276
1277 const xml::ElementNode *pRootElem = doc.getRootElement();
1278 if (strcmp(pRootElem->getName(), "Envelope"))
1279 return setError(VBOX_E_FILE_ERROR,
1280 tr("Root element in OVF file must be \"Envelope\"."));
1281
1282 // OVF has the following rough layout:
1283 /*
1284 -- <References> .... files referenced from other parts of the file, such as VMDK images
1285 -- Metadata, comprised of several section commands
1286 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
1287 -- optionally <Strings> for localization
1288 */
1289
1290 // get all "File" child elements of "References" section so we can look up files easily;
1291 // first find the "References" sections so we can look up files
1292 xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes
1293 const xml::ElementNode *pReferencesElem;
1294 if ((pReferencesElem = pRootElem->findChildElement("References")))
1295 pReferencesElem->getChildElements(listFileElements, "File");
1296
1297 // now go though the sections
1298 if (!(SUCCEEDED(rc = LoopThruSections(m->strPath.raw(), pReferencesElem, pRootElem))))
1299 return rc;
1300 }
1301 catch(xml::Error &x)
1302 {
1303 return setError(VBOX_E_FILE_ERROR,
1304 x.what());
1305 }
1306
1307 return S_OK;
1308}
1309
1310/**
1311 * Public method implementation.
1312 * @return
1313 */
1314STDMETHODIMP Appliance::Interpret()
1315{
1316 // @todo:
1317 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
1318 // - Appropriate handle errors like not supported file formats
1319 AutoCaller autoCaller(this);
1320 CheckComRCReturnRC(autoCaller.rc());
1321
1322 AutoWriteLock(this);
1323
1324 HRESULT rc = S_OK;
1325
1326 /* Clear any previous virtual system descriptions */
1327 m->virtualSystemDescriptions.clear();
1328
1329 /* We need the default path for storing disk images */
1330 ComPtr<ISystemProperties> systemProps;
1331 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
1332 CheckComRCReturnRC(rc);
1333 Bstr bstrDefaultHardDiskLocation;
1334 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
1335 CheckComRCReturnRC(rc);
1336
1337 /* Try/catch so we can clean up on error */
1338 try
1339 {
1340 list<VirtualSystem>::const_iterator it;
1341 /* Iterate through all virtual systems */
1342 for (it = m->llVirtualSystems.begin();
1343 it != m->llVirtualSystems.end();
1344 ++it)
1345 {
1346 const VirtualSystem &vsysThis = *it;
1347
1348 ComObjPtr<VirtualSystemDescription> pNewDesc;
1349 rc = pNewDesc.createObject();
1350 CheckComRCThrowRC(rc);
1351 rc = pNewDesc->init();
1352 CheckComRCThrowRC(rc);
1353
1354 /* Guest OS type */
1355 Utf8Str strOsTypeVBox,
1356 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
1357 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);
1358 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
1359 "",
1360 strCIMOSType,
1361 strOsTypeVBox);
1362
1363 /* VM name */
1364 /* If the there isn't any name specified create a default one out of
1365 * the OS type */
1366 Utf8Str nameVBox = vsysThis.strName;
1367 if (nameVBox.isEmpty())
1368 nameVBox = strOsTypeVBox;
1369 searchUniqueVMName(nameVBox);
1370 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
1371 "",
1372 vsysThis.strName,
1373 nameVBox);
1374
1375 /* VM Product */
1376 if (!vsysThis.strProduct.isEmpty())
1377 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,
1378 "",
1379 vsysThis.strProduct,
1380 vsysThis.strProduct);
1381
1382 /* VM Vendor */
1383 if (!vsysThis.strVendor.isEmpty())
1384 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,
1385 "",
1386 vsysThis.strVendor,
1387 vsysThis.strVendor);
1388
1389 /* VM Version */
1390 if (!vsysThis.strVersion.isEmpty())
1391 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,
1392 "",
1393 vsysThis.strVersion,
1394 vsysThis.strVersion);
1395
1396 /* VM ProductUrl */
1397 if (!vsysThis.strProductUrl.isEmpty())
1398 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,
1399 "",
1400 vsysThis.strProductUrl,
1401 vsysThis.strProductUrl);
1402
1403 /* VM VendorUrl */
1404 if (!vsysThis.strVendorUrl.isEmpty())
1405 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,
1406 "",
1407 vsysThis.strVendorUrl,
1408 vsysThis.strVendorUrl);
1409
1410 /* VM description */
1411 if (!vsysThis.strDescription.isEmpty())
1412 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
1413 "",
1414 vsysThis.strDescription,
1415 vsysThis.strDescription);
1416
1417 /* VM license */
1418 if (!vsysThis.strLicenseText.isEmpty())
1419 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
1420 "",
1421 vsysThis.strLicenseText,
1422 vsysThis.strLicenseText);
1423
1424 /* Now that we know the OS type, get our internal defaults based on that. */
1425 ComPtr<IGuestOSType> pGuestOSType;
1426 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
1427 CheckComRCThrowRC(rc);
1428
1429 /* CPU count */
1430 ULONG cpuCountVBox = vsysThis.cCPUs;
1431 /* Check for the constrains */
1432 if (cpuCountVBox > 1) //SchemaDefs::MaxCPUCount)
1433 {
1434 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
1435 vsysThis.strName.c_str(), cpuCountVBox, 1); //SchemaDefs::MaxCPUCount);
1436 cpuCountVBox = 1; //SchemaDefs::MaxCPUCount;
1437 }
1438 if (vsysThis.cCPUs == 0)
1439 cpuCountVBox = 1;
1440 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
1441 "",
1442 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
1443 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
1444
1445 /* RAM */
1446 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
1447 /* Check for the constrains */
1448 if (ullMemSizeVBox != 0 &&
1449 (ullMemSizeVBox < MM_RAM_MIN_IN_MB ||
1450 ullMemSizeVBox > MM_RAM_MAX_IN_MB))
1451 {
1452 addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."),
1453 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1454 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
1455 }
1456 if (vsysThis.ullMemorySize == 0)
1457 {
1458 /* If the RAM of the OVF is zero, use our predefined values */
1459 ULONG memSizeVBox2;
1460 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
1461 CheckComRCThrowRC(rc);
1462 /* VBox stores that in MByte */
1463 ullMemSizeVBox = (uint64_t)memSizeVBox2;
1464 }
1465 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
1466 "",
1467 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
1468 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
1469
1470 /* Audio */
1471 if (!vsysThis.strSoundCardType.isNull())
1472 /* Currently we set the AC97 always.
1473 @todo: figure out the hardware which could be possible */
1474 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
1475 "",
1476 vsysThis.strSoundCardType,
1477 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
1478
1479#ifdef VBOX_WITH_USB
1480 /* USB Controller */
1481 if (vsysThis.fHasUsbController)
1482 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
1483#endif /* VBOX_WITH_USB */
1484
1485 /* Network Controller */
1486 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();
1487 if (cEthernetAdapters > 0)
1488 {
1489 /* Check for the constrains */
1490 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)
1491 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
1492 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);
1493
1494 /* Get the default network adapter type for the selected guest OS */
1495 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
1496 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
1497 CheckComRCThrowRC(rc);
1498
1499 EthernetAdaptersList::const_iterator itEA;
1500 /* Iterate through all abstract networks. We support 8 network
1501 * adapters at the maximum, so the first 8 will be added only. */
1502 size_t a = 0;
1503 for (itEA = vsysThis.llEthernetAdapters.begin();
1504 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
1505 ++itEA, ++a)
1506 {
1507 const EthernetAdapter &ea = *itEA; // logical network to connect to
1508 Utf8Str strNetwork = ea.strNetworkName;
1509 // make sure it's one of these two
1510 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
1511 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
1512 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
1513 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
1514 )
1515 strNetwork = "Bridged"; // VMware assumes this is the default apparently
1516
1517 /* Figure out the hardware type */
1518 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
1519 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
1520 {
1521 /* If the default adapter is already one of the two
1522 * PCNet adapters use the default one. If not use the
1523 * Am79C970A as fallback. */
1524 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
1525 defaultAdapterVBox == NetworkAdapterType_Am79C973))
1526 nwAdapterVBox = NetworkAdapterType_Am79C970A;
1527 }
1528#ifdef VBOX_WITH_E1000
1529 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
1530 {
1531 /* If the default adapter is already one of the three
1532 * E1000 adapters use the default one. If not use the
1533 * I82540EM as fallback. */
1534 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
1535 defaultAdapterVBox == NetworkAdapterType_I82543GC ||
1536 defaultAdapterVBox == NetworkAdapterType_I82545EM))
1537 nwAdapterVBox = NetworkAdapterType_I82540EM;
1538 }
1539#endif /* VBOX_WITH_E1000 */
1540
1541 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
1542 "", // ref
1543 ea.strNetworkName, // orig
1544 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
1545 0,
1546 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
1547 }
1548 }
1549
1550 /* Floppy Drive */
1551 if (vsysThis.fHasFloppyDrive)
1552 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
1553
1554 /* CD Drive */
1555 /* @todo: I can't disable the CDROM. So nothing to do for now */
1556 /*
1557 if (vsysThis.fHasCdromDrive)
1558 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");*/
1559
1560 /* Hard disk Controller */
1561 uint16_t cIDEused = 0;
1562 uint16_t cSATAused = 0;
1563 uint16_t cSCSIused = 0;
1564 ControllersMap::const_iterator hdcIt;
1565 /* Iterate through all hard disk controllers */
1566 for (hdcIt = vsysThis.mapControllers.begin();
1567 hdcIt != vsysThis.mapControllers.end();
1568 ++hdcIt)
1569 {
1570 const HardDiskController &hdc = hdcIt->second;
1571 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
1572
1573 switch (hdc.system)
1574 {
1575 case HardDiskController::IDE:
1576 {
1577 /* Check for the constrains */
1578 /* @todo: I'm very confused! Are these bits *one* controller or
1579 is every port/bus declared as an extra controller. */
1580 if (cIDEused < 4)
1581 {
1582 // @todo: figure out the IDE types
1583 /* Use PIIX4 as default */
1584 Utf8Str strType = "PIIX4";
1585 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
1586 strType = "PIIX3";
1587 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
1588 strType = "ICH6";
1589 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
1590 strControllerID,
1591 hdc.strControllerType,
1592 strType);
1593 }
1594 else
1595 {
1596 /* Warn only once */
1597 if (cIDEused == 1)
1598 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
1599 vsysThis.strName.c_str());
1600
1601 }
1602 ++cIDEused;
1603 break;
1604 }
1605
1606#ifdef VBOX_WITH_AHCI
1607 case HardDiskController::SATA:
1608 {
1609 /* Check for the constrains */
1610 if (cSATAused < 1)
1611 {
1612 // @todo: figure out the SATA types
1613 /* We only support a plain AHCI controller, so use them always */
1614 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
1615 strControllerID,
1616 hdc.strControllerType,
1617 "AHCI");
1618 }
1619 else
1620 {
1621 /* Warn only once */
1622 if (cSATAused == 1)
1623 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
1624 vsysThis.strName.c_str());
1625
1626 }
1627 ++cSATAused;
1628 break;
1629 }
1630#endif /* VBOX_WITH_AHCI */
1631
1632 case HardDiskController::SCSI:
1633 {
1634 /* Check for the constrains */
1635 if (cSCSIused < 1)
1636 {
1637 Utf8Str hdcController = "LsiLogic";
1638 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
1639 hdcController = "BusLogic";
1640 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
1641 strControllerID,
1642 hdc.strControllerType,
1643 hdcController);
1644 }
1645 else
1646 addWarning(tr("The virtual system \"%s\" requests support for an additional SCSI controller of type \"%s\" with ID %s, but VirtualBox presently supports only one SCSI controller."),
1647 vsysThis.strName.c_str(),
1648 hdc.strControllerType.c_str(),
1649 strControllerID.c_str());
1650 ++cSCSIused;
1651 break;
1652 }
1653 }
1654 }
1655
1656 /* Hard disks */
1657 if (vsysThis.mapVirtualDisks.size() > 0)
1658 {
1659 VirtualDisksMap::const_iterator itVD;
1660 /* Iterate through all hard disks ()*/
1661 for (itVD = vsysThis.mapVirtualDisks.begin();
1662 itVD != vsysThis.mapVirtualDisks.end();
1663 ++itVD)
1664 {
1665 const VirtualDisk &hd = itVD->second;
1666 /* Get the associated disk image */
1667 const DiskImage &di = m->mapDisks[hd.strDiskId];
1668
1669 // @todo:
1670 // - figure out all possible vmdk formats we also support
1671 // - figure out if there is a url specifier for vhd already
1672 // - we need a url specifier for the vdi format
1673 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1674 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
1675 {
1676 /* If the href is empty use the VM name as filename */
1677 Utf8Str strFilename = di.strHref;
1678 if (!strFilename.length())
1679 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
1680 /* Construct a unique target path */
1681 Utf8StrFmt strPath("%ls%c%s",
1682 bstrDefaultHardDiskLocation.raw(),
1683 RTPATH_DELIMITER,
1684 strFilename.c_str());
1685 searchUniqueDiskImageFilePath(strPath);
1686
1687 /* find the description for the hard disk controller
1688 * that has the same ID as hd.idController */
1689 const VirtualSystemDescriptionEntry *pController;
1690 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
1691 throw setError(E_FAIL,
1692 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
1693 hd.idController,
1694 di.strHref.c_str());
1695
1696 /* controller to attach to, and the bus within that controller */
1697 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
1698 pController->ulIndex,
1699 hd.ulAddressOnParent);
1700 ULONG ulSize = 0;
1701 if (di.iCapacity != -1)
1702 ulSize = (ULONG)(di.iCapacity / _1M);
1703 else if (di.iPopulatedSize != -1)
1704 ulSize = (ULONG)(di.iPopulatedSize / _1M);
1705 else if (di.iSize != -1)
1706 ulSize = (ULONG)(di.iSize / _1M);
1707 if (ulSize == 0)
1708 ulSize = 10000; // assume 10 GB, this is for the progress bar only anyway
1709 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
1710 hd.strDiskId,
1711 di.strHref,
1712 strPath,
1713 ulSize,
1714 strExtraConfig);
1715 }
1716 else
1717 throw setError(VBOX_E_FILE_ERROR,
1718 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
1719 }
1720 }
1721
1722 m->virtualSystemDescriptions.push_back(pNewDesc);
1723 }
1724 }
1725 catch (HRESULT aRC)
1726 {
1727 /* On error we clear the list & return */
1728 m->virtualSystemDescriptions.clear();
1729 rc = aRC;
1730 }
1731
1732 return rc;
1733}
1734
1735struct Appliance::TaskImportMachines
1736{
1737 TaskImportMachines(Appliance *aThat, Progress *aProgress)
1738 : pAppliance(aThat)
1739 , progress(aProgress)
1740 , rc(S_OK)
1741 {}
1742 ~TaskImportMachines() {}
1743
1744 HRESULT startThread();
1745
1746 Appliance *pAppliance;
1747 ComObjPtr<Progress> progress;
1748 HRESULT rc;
1749};
1750
1751HRESULT Appliance::TaskImportMachines::startThread()
1752{
1753 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportMachines, this,
1754 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1755 "Appliance::Task");
1756 ComAssertMsgRCRet(vrc,
1757 ("Could not create taskThreadImportMachines (%Rrc)\n", vrc), E_FAIL);
1758
1759 return S_OK;
1760}
1761
1762/**
1763 * Public method implementation.
1764 * @param aProgress
1765 * @return
1766 */
1767STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
1768{
1769 CheckComArgOutPointerValid(aProgress);
1770
1771 AutoCaller autoCaller(this);
1772 CheckComRCReturnRC(autoCaller.rc());
1773
1774 AutoReadLock(this);
1775
1776 HRESULT rc = S_OK;
1777
1778 ComObjPtr<Progress> progress;
1779 try
1780 {
1781 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
1782 m->strPath.raw());
1783 rc = setUpProgress(progress, progressDesc);
1784 if (FAILED(rc)) throw rc;
1785
1786 /* Initialize our worker task */
1787 std::auto_ptr<TaskImportMachines> task(new TaskImportMachines(this, progress));
1788 //AssertComRCThrowRC (task->autoCaller.rc());
1789
1790 rc = task->startThread();
1791 if (FAILED(rc)) throw rc;
1792
1793 task.release();
1794 }
1795 catch (HRESULT aRC)
1796 {
1797 rc = aRC;
1798 }
1799
1800 if (SUCCEEDED(rc))
1801 /* Return progress to the caller */
1802 progress.queryInterfaceTo(aProgress);
1803
1804 return rc;
1805}
1806
1807struct MyHardDiskAttachment
1808{
1809 Guid uuid;
1810 ComPtr<IMachine> pMachine;
1811 Bstr controllerType;
1812 int32_t lChannel;
1813 int32_t lDevice;
1814};
1815
1816/**
1817 * Worker thread implementation for ImportMachines().
1818 * @param aThread
1819 * @param pvUser
1820 */
1821/* static */
1822DECLCALLBACK(int) Appliance::taskThreadImportMachines(RTTHREAD /* aThread */, void *pvUser)
1823{
1824 std::auto_ptr<TaskImportMachines> task(static_cast<TaskImportMachines*>(pvUser));
1825 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1826
1827 Appliance *pAppliance = task->pAppliance;
1828
1829 LogFlowFuncEnter();
1830 LogFlowFunc(("Appliance %p\n", pAppliance));
1831
1832 AutoCaller autoCaller(pAppliance);
1833 CheckComRCReturnRC(autoCaller.rc());
1834
1835 AutoWriteLock appLock(pAppliance);
1836
1837 HRESULT rc = S_OK;
1838
1839 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
1840
1841 // rollback for errors:
1842 // 1) a list of images that we created/imported
1843 list<MyHardDiskAttachment> llHardDiskAttachments;
1844 list< ComPtr<IHardDisk> > llHardDisksCreated;
1845 list<Guid> llMachinesRegistered;
1846
1847 ComPtr<ISession> session;
1848 bool fSessionOpen = false;
1849 rc = session.createInprocObject(CLSID_Session);
1850 CheckComRCReturnRC(rc);
1851
1852 list<VirtualSystem>::const_iterator it;
1853 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1854 /* Iterate through all virtual systems of that appliance */
1855 size_t i = 0;
1856 for (it = pAppliance->m->llVirtualSystems.begin(),
1857 it1 = pAppliance->m->virtualSystemDescriptions.begin();
1858 it != pAppliance->m->llVirtualSystems.end();
1859 ++it, ++it1, ++i)
1860 {
1861 const VirtualSystem &vsysThis = *it;
1862 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1863
1864 ComPtr<IMachine> pNewMachine;
1865
1866 /* Catch possible errors */
1867 try
1868 {
1869 /* Guest OS type */
1870 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1871 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1872 if (vsdeOS.size() < 1)
1873 throw setError(VBOX_E_FILE_ERROR,
1874 tr("Missing guest OS type"));
1875 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1876
1877 /* Now that we know the base system get our internal defaults based on that. */
1878 ComPtr<IGuestOSType> osType;
1879 rc = pVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1880 if (FAILED(rc)) throw rc;
1881
1882 /* Create the machine */
1883 /* First get the name */
1884 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1885 if (vsdeName.size() < 1)
1886 throw setError(VBOX_E_FILE_ERROR,
1887 tr("Missing VM name"));
1888 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1889 rc = pVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1890 Bstr(), Guid(),
1891 pNewMachine.asOutParam());
1892 if (FAILED(rc)) throw rc;
1893
1894 // and the description
1895 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
1896 if (vsdeDescription.size())
1897 {
1898 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;
1899 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));
1900 if (FAILED(rc)) throw rc;
1901 }
1902
1903 /* CPU count (ignored for now) */
1904 // EntriesList vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1905
1906 /* RAM */
1907 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1908 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1909 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1910 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1911 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1912 if (FAILED(rc)) throw rc;
1913
1914 /* VRAM */
1915 /* Get the recommended VRAM for this guest OS type */
1916 ULONG vramVBox;
1917 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1918 if (FAILED(rc)) throw rc;
1919
1920 /* Set the VRAM */
1921 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1922 if (FAILED(rc)) throw rc;
1923
1924 /* Audio Adapter */
1925 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1926 /* @todo: we support one audio adapter only */
1927 if (vsdeAudioAdapter.size() > 0)
1928 {
1929 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1930 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1931 {
1932 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1933 ComPtr<IAudioAdapter> audioAdapter;
1934 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1935 if (FAILED(rc)) throw rc;
1936 rc = audioAdapter->COMSETTER(Enabled)(true);
1937 if (FAILED(rc)) throw rc;
1938 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1939 if (FAILED(rc)) throw rc;
1940 }
1941 }
1942
1943#ifdef VBOX_WITH_USB
1944 /* USB Controller */
1945 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1946 // USB support is enabled if there's at least one such entry; to disable USB support,
1947 // the type of the USB item would have been changed to "ignore"
1948 bool fUSBEnabled = vsdeUSBController.size() > 0;
1949
1950 ComPtr<IUSBController> usbController;
1951 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1952 if (FAILED(rc)) throw rc;
1953 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1954 if (FAILED(rc)) throw rc;
1955#endif /* VBOX_WITH_USB */
1956
1957 /* Change the network adapters */
1958 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1959 if (vsdeNW.size() == 0)
1960 {
1961 /* No network adapters, so we have to disable our default one */
1962 ComPtr<INetworkAdapter> nwVBox;
1963 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1964 if (FAILED(rc)) throw rc;
1965 rc = nwVBox->COMSETTER(Enabled)(false);
1966 if (FAILED(rc)) throw rc;
1967 }
1968 else
1969 {
1970 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1971 /* Iterate through all network cards. We support 8 network adapters
1972 * at the maximum. (@todo: warn if there are more!) */
1973 size_t a = 0;
1974 for (nwIt = vsdeNW.begin();
1975 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1976 ++nwIt, ++a)
1977 {
1978 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1979
1980 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1981 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1982 ComPtr<INetworkAdapter> pNetworkAdapter;
1983 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1984 if (FAILED(rc)) throw rc;
1985 /* Enable the network card & set the adapter type */
1986 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1987 if (FAILED(rc)) throw rc;
1988 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1989 if (FAILED(rc)) throw rc;
1990
1991 // default is NAT; change to "bridged" if extra conf says so
1992 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1993 {
1994 /* Attach to the right interface */
1995 rc = pNetworkAdapter->AttachToBridgedInterface();
1996 if (FAILED(rc)) throw rc;
1997 ComPtr<IHost> host;
1998 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
1999 if (FAILED(rc)) throw rc;
2000 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
2001 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
2002 if (FAILED(rc)) throw rc;
2003 /* We search for the first host network interface which
2004 * is usable for bridged networking */
2005 for (size_t i=0; i < nwInterfaces.size(); ++i)
2006 {
2007 HostNetworkInterfaceType_T itype;
2008 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
2009 if (FAILED(rc)) throw rc;
2010 if (itype == HostNetworkInterfaceType_Bridged)
2011 {
2012 Bstr name;
2013 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
2014 if (FAILED(rc)) throw rc;
2015 /* Set the interface name to attach to */
2016 pNetworkAdapter->COMSETTER(HostInterface)(name);
2017 if (FAILED(rc)) throw rc;
2018 break;
2019 }
2020 }
2021 }
2022 /* Next test for host only interfaces */
2023 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))
2024 {
2025 /* Attach to the right interface */
2026 rc = pNetworkAdapter->AttachToHostOnlyInterface();
2027 if (FAILED(rc)) throw rc;
2028 ComPtr<IHost> host;
2029 rc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
2030 if (FAILED(rc)) throw rc;
2031 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
2032 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
2033 if (FAILED(rc)) throw rc;
2034 /* We search for the first host network interface which
2035 * is usable for host only networking */
2036 for (size_t i=0; i < nwInterfaces.size(); ++i)
2037 {
2038 HostNetworkInterfaceType_T itype;
2039 rc = nwInterfaces[i]->COMGETTER(InterfaceType)(&itype);
2040 if (FAILED(rc)) throw rc;
2041 if (itype == HostNetworkInterfaceType_HostOnly)
2042 {
2043 Bstr name;
2044 rc = nwInterfaces[i]->COMGETTER(Name)(name.asOutParam());
2045 if (FAILED(rc)) throw rc;
2046 /* Set the interface name to attach to */
2047 pNetworkAdapter->COMSETTER(HostInterface)(name);
2048 if (FAILED(rc)) throw rc;
2049 break;
2050 }
2051 }
2052 }
2053 }
2054 }
2055
2056 /* Floppy drive */
2057 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
2058 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
2059 // the type of the floppy item would have been changed to "ignore"
2060 bool fFloppyEnabled = vsdeFloppy.size() > 0;
2061 ComPtr<IFloppyDrive> floppyDrive;
2062 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
2063 if (FAILED(rc)) throw rc;
2064 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
2065 if (FAILED(rc)) throw rc;
2066
2067 /* CDROM drive */
2068 /* @todo: I can't disable the CDROM. So nothing to do for now */
2069 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
2070
2071 /* Hard disk controller IDE */
2072 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
2073 if (vsdeHDCIDE.size() > 1)
2074 throw setError(VBOX_E_FILE_ERROR,
2075 tr("Too many IDE controllers in OVF; VirtualBox only supports one"));
2076 if (vsdeHDCIDE.size() == 1)
2077 {
2078 ComPtr<IStorageController> pController;
2079 rc = pNewMachine->GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
2080 if (FAILED(rc)) throw rc;
2081
2082 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
2083 if (!strcmp(pcszIDEType, "PIIX3"))
2084 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
2085 else if (!strcmp(pcszIDEType, "PIIX4"))
2086 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
2087 else if (!strcmp(pcszIDEType, "ICH6"))
2088 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
2089 else
2090 throw setError(VBOX_E_FILE_ERROR,
2091 tr("Invalid IDE controller type \"%s\""),
2092 pcszIDEType);
2093 if (FAILED(rc)) throw rc;
2094 }
2095#ifdef VBOX_WITH_AHCI
2096 /* Hard disk controller SATA */
2097 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
2098 if (vsdeHDCSATA.size() > 1)
2099 throw setError(VBOX_E_FILE_ERROR,
2100 tr("Too many SATA controllers in OVF; VirtualBox only supports one"));
2101 if (vsdeHDCSATA.size() > 0)
2102 {
2103 ComPtr<IStorageController> pController;
2104 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
2105 if (hdcVBox == "AHCI")
2106 {
2107 rc = pNewMachine->AddStorageController(Bstr("SATA"), StorageBus_SATA, pController.asOutParam());
2108 if (FAILED(rc)) throw rc;
2109 }
2110 else
2111 throw setError(VBOX_E_FILE_ERROR,
2112 tr("Invalid SATA controller type \"%s\""),
2113 hdcVBox.c_str());
2114 }
2115#endif /* VBOX_WITH_AHCI */
2116
2117 /* Hard disk controller SCSI */
2118 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
2119 if (vsdeHDCSCSI.size() > 1)
2120 throw setError(VBOX_E_FILE_ERROR,
2121 tr("Too many SCSI controllers in OVF; VirtualBox only supports one"));
2122 if (vsdeHDCSCSI.size() > 0)
2123 {
2124 ComPtr<IStorageController> pController;
2125 StorageControllerType_T controllerType;
2126 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
2127 if (hdcVBox == "LsiLogic")
2128 controllerType = StorageControllerType_LsiLogic;
2129 else if (hdcVBox == "BusLogic")
2130 controllerType = StorageControllerType_BusLogic;
2131 else
2132 throw setError(VBOX_E_FILE_ERROR,
2133 tr("Invalid SCSI controller type \"%s\""),
2134 hdcVBox.c_str());
2135
2136 rc = pNewMachine->AddStorageController(Bstr("SCSI"), StorageBus_SCSI, pController.asOutParam());
2137 if (FAILED(rc)) throw rc;
2138 rc = pController->COMSETTER(ControllerType)(controllerType);
2139 if (FAILED(rc)) throw rc;
2140 }
2141
2142 /* Now its time to register the machine before we add any hard disks */
2143 rc = pVirtualBox->RegisterMachine(pNewMachine);
2144 if (FAILED(rc)) throw rc;
2145
2146 Guid newMachineId;
2147 rc = pNewMachine->COMGETTER(Id)(newMachineId.asOutParam());
2148 if (FAILED(rc)) throw rc;
2149
2150 // store new machine for roll-back in case of errors
2151 llMachinesRegistered.push_back(newMachineId);
2152
2153 /* Create the hard disks & connect them to the appropriate controllers. */
2154 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2155 if (avsdeHDs.size() > 0)
2156 {
2157 /* If in the next block an error occur we have to deregister
2158 the machine, so make an extra try/catch block. */
2159 ComPtr<IHardDisk> srcHdVBox;
2160 bool fSourceHdNeedsClosing = false;
2161
2162 try
2163 {
2164 /* In order to attach hard disks we need to open a session
2165 * for the new machine */
2166 rc = pVirtualBox->OpenSession(session, newMachineId);
2167 if (FAILED(rc)) throw rc;
2168 fSessionOpen = true;
2169
2170 /* The disk image has to be on the same place as the OVF file. So
2171 * strip the filename out of the full file path. */
2172 Utf8Str strSrcDir = stripFilename(pAppliance->m->strPath);
2173
2174 /* Iterate over all given disk images */
2175 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2176 for (itHD = avsdeHDs.begin();
2177 itHD != avsdeHDs.end();
2178 ++itHD)
2179 {
2180 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2181
2182 const char *pcszDstFilePath = vsdeHD->strVbox.c_str();
2183 /* Check if the destination file exists already or the
2184 * destination path is empty. */
2185 if ( !(*pcszDstFilePath)
2186 || RTPathExists(pcszDstFilePath)
2187 )
2188 /* This isn't allowed */
2189 throw setError(VBOX_E_FILE_ERROR,
2190 tr("Destination file '%s' exists",
2191 pcszDstFilePath));
2192
2193 /* Find the disk from the OVF's disk list */
2194 DiskImagesMap::const_iterator itDiskImage = pAppliance->m->mapDisks.find(vsdeHD->strRef);
2195 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2196 in the virtual system's disks map under that ID and also in the global images map. */
2197 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2198
2199 if ( itDiskImage == pAppliance->m->mapDisks.end()
2200 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
2201 )
2202 throw setError(E_FAIL,
2203 tr("Internal inconsistency looking up disk images."));
2204
2205 const DiskImage &di = itDiskImage->second;
2206 const VirtualDisk &vd = itVirtualDisk->second;
2207
2208 /* Make sure all target directories exists */
2209 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
2210 if (FAILED(rc))
2211 throw rc;
2212
2213 // subprogress object for hard disk
2214 ComPtr<IProgress> pProgress2;
2215
2216 ComPtr<IHardDisk> dstHdVBox;
2217 /* If strHref is empty we have to create a new file */
2218 if (di.strHref.isEmpty())
2219 {
2220 /* Which format to use? */
2221 Bstr srcFormat = L"VDI";
2222 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
2223 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive))
2224 srcFormat = L"VMDK";
2225 /* Create an empty hard disk */
2226 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2227 if (FAILED(rc)) throw rc;
2228
2229 /* Create a dynamic growing disk image with the given capacity */
2230 rc = dstHdVBox->CreateBaseStorage(di.iCapacity / _1M, HardDiskVariant_Standard, pProgress2.asOutParam());
2231 if (FAILED(rc)) throw rc;
2232
2233 /* Advance to the next operation */
2234 if (!task->progress.isNull())
2235 task->progress->setNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath),
2236 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally
2237 }
2238 else
2239 {
2240 /* Construct the source file path */
2241 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2242 /* Check if the source file exists */
2243 if (!RTPathExists(strSrcFilePath.c_str()))
2244 /* This isn't allowed */
2245 throw setError(VBOX_E_FILE_ERROR,
2246 tr("Source virtual disk image file '%s' doesn't exist"),
2247 strSrcFilePath.c_str());
2248
2249 /* Clone the disk image (this is necessary cause the id has
2250 * to be recreated for the case the same hard disk is
2251 * attached already from a previous import) */
2252
2253 /* First open the existing disk image */
2254 rc = pVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),
2255 AccessMode_ReadOnly,
2256 srcHdVBox.asOutParam());
2257 if (FAILED(rc)) throw rc;
2258 fSourceHdNeedsClosing = true;
2259
2260 /* We need the format description of the source disk image */
2261 Bstr srcFormat;
2262 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2263 if (FAILED(rc)) throw rc;
2264 /* Create a new hard disk interface for the destination disk image */
2265 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2266 if (FAILED(rc)) throw rc;
2267 /* Clone the source disk image */
2268 rc = srcHdVBox->CloneTo(dstHdVBox, HardDiskVariant_Standard, NULL, pProgress2.asOutParam());
2269 if (FAILED(rc)) throw rc;
2270
2271 /* Advance to the next operation */
2272 if (!task->progress.isNull())
2273 task->progress->setNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),
2274 vsdeHD->ulSizeMB); // operation's weight, as set up with the IProgress originally);
2275 }
2276
2277 // now wait for the background disk operation to complete; this throws HRESULTs on error
2278 pAppliance->waitForAsyncProgress(task->progress, pProgress2);
2279
2280 if (fSourceHdNeedsClosing)
2281 {
2282 rc = srcHdVBox->Close();
2283 if (FAILED(rc)) throw rc;
2284 fSourceHdNeedsClosing = false;
2285 }
2286
2287 llHardDisksCreated.push_back(dstHdVBox);
2288 /* Now use the new uuid to attach the disk image to our new machine */
2289 ComPtr<IMachine> sMachine;
2290 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2291 if (FAILED(rc)) throw rc;
2292 Guid hdId;
2293 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
2294 if (FAILED(rc)) throw rc;
2295
2296 /* For now we assume we have one controller of every type only */
2297 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2298
2299 // this is for rollback later
2300 MyHardDiskAttachment mhda;
2301 mhda.uuid = newMachineId;
2302 mhda.pMachine = pNewMachine;
2303
2304 switch (hdc.system)
2305 {
2306 case HardDiskController::IDE:
2307 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
2308 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
2309 // the device number can be either 0 or 1, to specify the master or the slave device,
2310 // respectively. For the secondary IDE controller, the device number is always 1 because
2311 // the master device is reserved for the CD-ROM drive.
2312 mhda.controllerType = Bstr("IDE");
2313 switch (vd.ulAddressOnParent)
2314 {
2315 case 0: // interpret this as primary master
2316 mhda.lChannel = (long)0;
2317 mhda.lDevice = (long)0;
2318 break;
2319
2320 case 1: // interpret this as primary slave
2321 mhda.lChannel = (long)0;
2322 mhda.lDevice = (long)1;
2323 break;
2324
2325 case 2: // interpret this as secondary slave
2326 mhda.lChannel = (long)1;
2327 mhda.lDevice = (long)1;
2328 break;
2329
2330 default:
2331 throw setError(VBOX_E_NOT_SUPPORTED,
2332 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent);
2333 break;
2334 }
2335 break;
2336
2337 case HardDiskController::SATA:
2338 mhda.controllerType = Bstr("SATA");
2339 mhda.lChannel = (long)vd.ulAddressOnParent;
2340 mhda.lDevice = (long)0;
2341 break;
2342
2343 case HardDiskController::SCSI:
2344 mhda.controllerType = Bstr("SCSI");
2345 mhda.lChannel = (long)vd.ulAddressOnParent;
2346 mhda.lDevice = (long)0;
2347 break;
2348
2349 default: break;
2350 }
2351
2352 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice));
2353
2354 rc = sMachine->AttachHardDisk(hdId,
2355 mhda.controllerType,
2356 mhda.lChannel,
2357 mhda.lDevice);
2358 if (FAILED(rc)) throw rc;
2359
2360 llHardDiskAttachments.push_back(mhda);
2361
2362 rc = sMachine->SaveSettings();
2363 if (FAILED(rc)) throw rc;
2364 } // end for (itHD = avsdeHDs.begin();
2365
2366 // only now that we're done with all disks, close the session
2367 rc = session->Close();
2368 if (FAILED(rc)) throw rc;
2369 fSessionOpen = false;
2370 }
2371 catch(HRESULT /* aRC */)
2372 {
2373 if (fSourceHdNeedsClosing)
2374 srcHdVBox->Close();
2375
2376 if (fSessionOpen)
2377 session->Close();
2378
2379 throw;
2380 }
2381 }
2382 }
2383 catch(HRESULT aRC)
2384 {
2385 rc = aRC;
2386 }
2387
2388 if (FAILED(rc))
2389 break;
2390
2391 } // for (it = pAppliance->m->llVirtualSystems.begin(),
2392
2393 if (FAILED(rc))
2394 {
2395 // with _whatever_ error we've had, do a complete roll-back of
2396 // machines and disks we've created; unfortunately this is
2397 // not so trivially done...
2398
2399 HRESULT rc2;
2400 // detach all hard disks from all machines we created
2401 list<MyHardDiskAttachment>::iterator itM;
2402 for (itM = llHardDiskAttachments.begin();
2403 itM != llHardDiskAttachments.end();
2404 ++itM)
2405 {
2406 const MyHardDiskAttachment &mhda = *itM;
2407 rc2 = pVirtualBox->OpenSession(session, mhda.uuid);
2408 if (SUCCEEDED(rc2))
2409 {
2410 ComPtr<IMachine> sMachine;
2411 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
2412 if (SUCCEEDED(rc2))
2413 {
2414 rc2 = sMachine->DetachHardDisk(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
2415 rc2 = sMachine->SaveSettings();
2416 }
2417 session->Close();
2418 }
2419 }
2420
2421 // now clean up all hard disks we created
2422 list< ComPtr<IHardDisk> >::iterator itHD;
2423 for (itHD = llHardDisksCreated.begin();
2424 itHD != llHardDisksCreated.end();
2425 ++itHD)
2426 {
2427 ComPtr<IHardDisk> pDisk = *itHD;
2428 ComPtr<IProgress> pProgress;
2429 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
2430 rc2 = pProgress->WaitForCompletion(-1);
2431 }
2432
2433 // finally, deregister and remove all machines
2434 list<Guid>::iterator itID;
2435 for (itID = llMachinesRegistered.begin();
2436 itID != llMachinesRegistered.end();
2437 ++itID)
2438 {
2439 const Guid &guid = *itID;
2440 ComPtr<IMachine> failedMachine;
2441 rc2 = pVirtualBox->UnregisterMachine(guid, failedMachine.asOutParam());
2442 if (SUCCEEDED(rc2))
2443 rc2 = failedMachine->DeleteSettings();
2444 }
2445 }
2446
2447 task->rc = rc;
2448
2449 if (!task->progress.isNull())
2450 task->progress->notifyComplete(rc);
2451
2452 LogFlowFunc(("rc=%Rhrc\n", rc));
2453 LogFlowFuncLeave();
2454
2455 return VINF_SUCCESS;
2456}
2457
2458struct Appliance::TaskWriteOVF
2459{
2460 TaskWriteOVF(Appliance *aThat, Progress *aProgress)
2461 : pAppliance(aThat),
2462 enFormat(unspecified),
2463 progress(aProgress),
2464 rc(S_OK)
2465 {}
2466 ~TaskWriteOVF() {}
2467
2468 HRESULT startThread();
2469
2470 Appliance *pAppliance;
2471 enum { unspecified, OVF_0_9, OVF_1_0 }
2472 enFormat;
2473
2474 ComObjPtr<Progress> progress;
2475 HRESULT rc;
2476};
2477
2478HRESULT Appliance::TaskWriteOVF::startThread()
2479{
2480 int vrc = RTThreadCreate(NULL, Appliance::taskThreadWriteOVF, this,
2481 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
2482 "Appliance::Task");
2483 ComAssertMsgRCRet(vrc,
2484 ("Could not create taskThreadExportOVF (%Rrc)\n", vrc), E_FAIL);
2485
2486 return S_OK;
2487}
2488
2489STDMETHODIMP Appliance::Write(IN_BSTR format, IN_BSTR path, IProgress **aProgress)
2490{
2491 HRESULT rc = S_OK;
2492
2493 CheckComArgOutPointerValid(aProgress);
2494
2495 AutoCaller autoCaller(this);
2496 if (FAILED(rc = autoCaller.rc())) return rc;
2497
2498 AutoWriteLock(this);
2499
2500 // see if we can handle this file; for now we insist it has an ".ovf" extension
2501 m->strPath = path;
2502 if (!m->strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
2503 return setError(VBOX_E_FILE_ERROR,
2504 tr("Appliance file must have .ovf extension"));
2505
2506 ComObjPtr<Progress> progress;
2507 try
2508 {
2509 Bstr progressDesc = BstrFmt(tr("Export appliance '%s'"),
2510 m->strPath.raw());
2511 rc = setUpProgress(progress, progressDesc);
2512 if (FAILED(rc)) throw rc;
2513
2514 /* Initialize our worker task */
2515 std::auto_ptr<TaskWriteOVF> task(new TaskWriteOVF(this, progress));
2516 //AssertComRCThrowRC (task->autoCaller.rc());
2517
2518 Utf8Str strFormat(format);
2519 if (strFormat == "ovf-0.9")
2520 task->enFormat = TaskWriteOVF::OVF_0_9;
2521 else if (strFormat == "ovf-1.0")
2522 task->enFormat = TaskWriteOVF::OVF_1_0;
2523 else
2524 return setError(VBOX_E_FILE_ERROR,
2525 tr("Invalid format \"%s\" specified"), strFormat.c_str());
2526
2527 rc = task->startThread();
2528 CheckComRCThrowRC(rc);
2529
2530 task.release();
2531 }
2532 catch (HRESULT aRC)
2533 {
2534 rc = aRC;
2535 }
2536
2537 if (SUCCEEDED(rc))
2538 /* Return progress to the caller */
2539 progress.queryInterfaceTo(aProgress);
2540
2541 return rc;
2542}
2543
2544/**
2545 * Worker thread implementation for Write() (ovf writer).
2546 * @param aThread
2547 * @param pvUser
2548 */
2549/* static */
2550DECLCALLBACK(int) Appliance::taskThreadWriteOVF(RTTHREAD /* aThread */, void *pvUser)
2551{
2552 std::auto_ptr<TaskWriteOVF> task(static_cast<TaskWriteOVF*>(pvUser));
2553 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
2554
2555 Appliance *pAppliance = task->pAppliance;
2556
2557 LogFlowFuncEnter();
2558 LogFlowFunc(("Appliance %p\n", pAppliance));
2559
2560 AutoCaller autoCaller(pAppliance);
2561 CheckComRCReturnRC(autoCaller.rc());
2562
2563 AutoWriteLock appLock(pAppliance);
2564
2565 HRESULT rc = S_OK;
2566
2567 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
2568
2569 try
2570 {
2571 xml::Document doc;
2572 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
2573
2574 pelmRoot->setAttribute("ovf:version", (task->enFormat == TaskWriteOVF::OVF_1_0) ? "1.0" : "0.9");
2575 pelmRoot->setAttribute("xml:lang", "en-US");
2576
2577 Utf8Str strNamespace = (TaskWriteOVF::OVF_0_9)
2578 ? "http://www.vmware.com/schema/ovf/1/envelope" // 0.9
2579 : "http://schemas.dmtf.org/ovf/envelope/1"; // 1.0
2580 pelmRoot->setAttribute("xmlns", strNamespace);
2581 pelmRoot->setAttribute("xmlns:ovf", strNamespace);
2582
2583// pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
2584 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
2585 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
2586 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2587// pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
2588
2589 // <Envelope>/<References>
2590 xml::ElementNode *pelmReferences = pelmRoot->createChild("References"); // 0.9 and 1.0
2591
2592 /* <Envelope>/<DiskSection>:
2593 <DiskSection>
2594 <Info>List of the virtual disks used in the package</Info>
2595 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
2596 </DiskSection> */
2597 xml::ElementNode *pelmDiskSection;
2598 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2599 {
2600 // <Section xsi:type="ovf:DiskSection_Type">
2601 pelmDiskSection = pelmRoot->createChild("Section");
2602 pelmDiskSection->setAttribute("xsi:type", "ovf:DiskSection_Type");
2603 }
2604 else
2605 pelmDiskSection = pelmRoot->createChild("DiskSection");
2606
2607 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
2608 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
2609 // for now, set up a map so we have a list of unique disk names (to make
2610 // sure the same disk name is only added once)
2611 map<Utf8Str, const VirtualSystemDescriptionEntry*> mapDisks;
2612
2613 /* <Envelope>/<NetworkSection>:
2614 <NetworkSection>
2615 <Info>Logical networks used in the package</Info>
2616 <Network ovf:name="VM Network">
2617 <Description>The network that the LAMP Service will be available on</Description>
2618 </Network>
2619 </NetworkSection> */
2620 xml::ElementNode *pelmNetworkSection;
2621 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2622 {
2623 // <Section xsi:type="ovf:NetworkSection_Type">
2624 pelmNetworkSection = pelmRoot->createChild("Section");
2625 pelmNetworkSection->setAttribute("xsi:type", "ovf:NetworkSection_Type");
2626 }
2627 else
2628 pelmNetworkSection = pelmRoot->createChild("NetworkSection");
2629
2630 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
2631 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
2632 // for now, set up a map so we have a list of unique network names (to make
2633 // sure the same network name is only added once)
2634 map<Utf8Str, bool> mapNetworks;
2635 // we fill this later below when we iterate over the networks
2636
2637 // and here come the virtual systems:
2638
2639 // write a collection if we have more than one virtual system _and_ we're
2640 // writing OVF 1.0; otherwise fail since ovftool can't import more than
2641 // one machine, it seems
2642 xml::ElementNode *pelmToAddVirtualSystemsTo;
2643 if (pAppliance->m->virtualSystemDescriptions.size() > 1)
2644 {
2645 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2646 return setError(VBOX_E_FILE_ERROR,
2647 tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0"));
2648
2649 pelmToAddVirtualSystemsTo = pelmRoot->createChild("VirtualSystemCollection");
2650 /* xml::AttributeNode *pattrVirtualSystemCollectionId = */ pelmToAddVirtualSystemsTo->setAttribute("ovf:name", "ExportedVirtualBoxMachines"); // whatever
2651 }
2652 else
2653 pelmToAddVirtualSystemsTo = pelmRoot; // add virtual system directly under root element
2654
2655 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2656 /* Iterate through all virtual systems of that appliance */
2657 for (it = pAppliance->m->virtualSystemDescriptions.begin();
2658 it != pAppliance->m->virtualSystemDescriptions.end();
2659 ++it)
2660 {
2661 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2662
2663 xml::ElementNode *pelmVirtualSystem;
2664 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2665 {
2666 // <Section xsi:type="ovf:NetworkSection_Type">
2667 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("Content");
2668 pelmVirtualSystem->setAttribute("xsi:type", "ovf:VirtualSystem_Type");
2669 }
2670 else
2671 pelmVirtualSystem = pelmToAddVirtualSystemsTo->createChild("VirtualSystem");
2672
2673 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");
2674
2675 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2676 if (llName.size() != 1)
2677 throw setError(VBOX_E_NOT_SUPPORTED,
2678 tr("Missing VM name"));
2679 Utf8Str &strVMName = llName.front()->strVbox;
2680 pelmVirtualSystem->setAttribute("ovf:id", strVMName);
2681
2682 // product info
2683 std::list<VirtualSystemDescriptionEntry*> llProduct = vsdescThis->findByType(VirtualSystemDescriptionType_Product);
2684 std::list<VirtualSystemDescriptionEntry*> llProductUrl = vsdescThis->findByType(VirtualSystemDescriptionType_ProductUrl);
2685 std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->findByType(VirtualSystemDescriptionType_Vendor);
2686 std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->findByType(VirtualSystemDescriptionType_VendorUrl);
2687 std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->findByType(VirtualSystemDescriptionType_Version);
2688 bool fProduct = llProduct.size() && !llProduct.front()->strVbox.isEmpty();
2689 bool fProductUrl = llProductUrl.size() && !llProductUrl.front()->strVbox.isEmpty();
2690 bool fVendor = llVendor.size() && !llVendor.front()->strVbox.isEmpty();
2691 bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.front()->strVbox.isEmpty();
2692 bool fVersion = llVersion.size() && !llVersion.front()->strVbox.isEmpty();
2693 if (fProduct ||
2694 fProductUrl ||
2695 fVersion ||
2696 fVendorUrl ||
2697 fVersion)
2698 {
2699 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
2700 <Info>Meta-information about the installed software</Info>
2701 <Product>VAtest</Product>
2702 <Vendor>SUN Microsystems</Vendor>
2703 <Version>10.0</Version>
2704 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
2705 <VendorUrl>http://www.sun.com</VendorUrl>
2706 </Section> */
2707 xml::ElementNode *pelmAnnotationSection;
2708 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2709 {
2710 // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
2711 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
2712 pelmAnnotationSection->setAttribute("xsi:type", "ovf:ProductSection_Type");
2713 }
2714 else
2715 pelmAnnotationSection = pelmVirtualSystem->createChild("ProductSection");
2716
2717 pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software");
2718 if (fProduct)
2719 pelmAnnotationSection->createChild("Product")->addContent(llProduct.front()->strVbox);
2720 if (fVendor)
2721 pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.front()->strVbox);
2722 if (fVersion)
2723 pelmAnnotationSection->createChild("Version")->addContent(llVersion.front()->strVbox);
2724 if (fProductUrl)
2725 pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.front()->strVbox);
2726 if (fVendorUrl)
2727 pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.front()->strVbox);
2728 }
2729
2730 // description
2731 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
2732 if (llDescription.size() &&
2733 !llDescription.front()->strVbox.isEmpty())
2734 {
2735 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
2736 <Info>A human-readable annotation</Info>
2737 <Annotation>Plan 9</Annotation>
2738 </Section> */
2739 xml::ElementNode *pelmAnnotationSection;
2740 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2741 {
2742 // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
2743 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
2744 pelmAnnotationSection->setAttribute("xsi:type", "ovf:AnnotationSection_Type");
2745 }
2746 else
2747 pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");
2748
2749 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");
2750 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVbox);
2751 }
2752
2753 // license
2754 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License);
2755 if (llLicense.size() &&
2756 !llLicense.front()->strVbox.isEmpty())
2757 {
2758 /* <EulaSection>
2759 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
2760 <License ovf:msgid="1">License terms can go in here.</License>
2761 </EulaSection> */
2762 xml::ElementNode *pelmEulaSection;
2763 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2764 {
2765 pelmEulaSection = pelmVirtualSystem->createChild("Section");
2766 pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type");
2767 }
2768 else
2769 pelmEulaSection = pelmVirtualSystem->createChild("EulaSection");
2770
2771 pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system");
2772 pelmEulaSection->createChild("License")->addContent(llLicense.front()->strVbox);
2773 }
2774
2775 // operating system
2776 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2777 if (llOS.size() != 1)
2778 throw setError(VBOX_E_NOT_SUPPORTED,
2779 tr("Missing OS type"));
2780 /* <OperatingSystemSection ovf:id="82">
2781 <Info>Guest Operating System</Info>
2782 <Description>Linux 2.6.x</Description>
2783 </OperatingSystemSection> */
2784 xml::ElementNode *pelmOperatingSystemSection;
2785 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2786 {
2787 pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section");
2788 pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type");
2789 }
2790 else
2791 pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
2792
2793 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
2794 pelmOperatingSystemSection->createChild("Info")->addContent("The kind of installed guest operating system");
2795// pelmOperatingSystemSection->createChild("Description")->addContent("blah"); // @todo
2796
2797 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
2798 xml::ElementNode *pelmVirtualHardwareSection;
2799 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2800 {
2801 // <Section xsi:type="ovf:VirtualHardwareSection_Type">
2802 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section");
2803 pelmVirtualHardwareSection->setAttribute("xsi:type", "ovf:VirtualHardwareSection_Type");
2804 }
2805 else
2806 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
2807
2808 pelmVirtualHardwareSection->createChild("Info")->addContent("Virtual hardware requirements for a virtual machine");
2809
2810 /* <System>
2811 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
2812 <vssd:ElementName>vmware</vssd:ElementName>
2813 <vssd:InstanceID>1</vssd:InstanceID>
2814 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
2815 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2816 </System> */
2817 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
2818
2819 // <vssd:InstanceId>0</vssd:InstanceId>
2820 pelmSystem->createChild("vssd:InstanceId")->addContent("0");
2821 // <vssd:VirtualSystemIdentifier>VAtest</vssd:VirtualSystemIdentifier>
2822 pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName);
2823 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2824 const char *pcszHardware = "virtualbox-2.2";
2825 if (task->enFormat == TaskWriteOVF::OVF_0_9)
2826 // pretend to be vmware compatible then
2827 pcszHardware = "vmx-6";
2828 pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware);
2829
2830 // loop thru all description entries twice; once to write out all
2831 // devices _except_ disk images, and a second time to assign the
2832 // disk images; this is because disk images need to reference
2833 // IDE controllers, and we can't know their instance IDs without
2834 // assigning them first
2835
2836 uint32_t idIDEController = 0;
2837 int32_t lIDEControllerIndex = 0;
2838 uint32_t idSATAController = 0;
2839 int32_t lSATAControllerIndex = 0;
2840 uint32_t idSCSIController = 0;
2841 int32_t lSCSIControllerIndex = 0;
2842
2843 uint32_t ulInstanceID = 1;
2844 uint32_t cDisks = 0;
2845
2846 for (size_t uLoop = 1;
2847 uLoop <= 2;
2848 ++uLoop)
2849 {
2850 int32_t lIndexThis = 0;
2851 list<VirtualSystemDescriptionEntry>::const_iterator itD;
2852 for (itD = vsdescThis->m->llDescriptions.begin();
2853 itD != vsdescThis->m->llDescriptions.end();
2854 ++itD, ++lIndexThis)
2855 {
2856 const VirtualSystemDescriptionEntry &desc = *itD;
2857
2858 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff
2859 Utf8Str strResourceSubType;
2860
2861 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
2862 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
2863
2864 uint32_t ulParent = 0;
2865
2866 int32_t lVirtualQuantity = -1;
2867 Utf8Str strAllocationUnits;
2868
2869 int32_t lAddress = -1;
2870 int32_t lBusNumber = -1;
2871 int32_t lAddressOnParent = -1;
2872
2873 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
2874 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
2875 Utf8Str strHostResource;
2876
2877 uint64_t uTemp;
2878
2879 switch (desc.type)
2880 {
2881 case VirtualSystemDescriptionType_CPU:
2882 /* <Item>
2883 <rasd:Caption>1 virtual CPU</rasd:Caption>
2884 <rasd:Description>Number of virtual CPUs</rasd:Description>
2885 <rasd:ElementName>virtual CPU</rasd:ElementName>
2886 <rasd:InstanceID>1</rasd:InstanceID>
2887 <rasd:ResourceType>3</rasd:ResourceType>
2888 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2889 </Item> */
2890 if (uLoop == 1)
2891 {
2892 strDescription = "Number of virtual CPUs";
2893 type = OVFResourceType_Processor; // 3
2894 lVirtualQuantity = 1;
2895 }
2896 break;
2897
2898 case VirtualSystemDescriptionType_Memory:
2899 /* <Item>
2900 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
2901 <rasd:Caption>256 MB of memory</rasd:Caption>
2902 <rasd:Description>Memory Size</rasd:Description>
2903 <rasd:ElementName>Memory</rasd:ElementName>
2904 <rasd:InstanceID>2</rasd:InstanceID>
2905 <rasd:ResourceType>4</rasd:ResourceType>
2906 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
2907 </Item> */
2908 if (uLoop == 1)
2909 {
2910 strDescription = "Memory Size";
2911 type = OVFResourceType_Memory; // 4
2912 desc.strVbox.toInt(uTemp);
2913 lVirtualQuantity = (int32_t)(uTemp / _1M);
2914 strAllocationUnits = "MegaBytes";
2915 strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity); // without this ovftool won't eat the item
2916 }
2917 break;
2918
2919 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2920 /* <Item>
2921 <rasd:Caption>ideController1</rasd:Caption>
2922 <rasd:Description>IDE Controller</rasd:Description>
2923 <rasd:InstanceId>5</rasd:InstanceId>
2924 <rasd:ResourceType>5</rasd:ResourceType>
2925 <rasd:Address>1</rasd:Address>
2926 <rasd:BusNumber>1</rasd:BusNumber>
2927 </Item> */
2928 if (uLoop == 1)
2929 {
2930 strDescription = "IDE Controller";
2931 type = OVFResourceType_IDEController; // 5
2932 strResourceSubType = desc.strVbox;
2933 // it seems that OVFTool always writes these two, and since we can only
2934 // have one IDE controller, we'll use this as well
2935 lAddress = 1;
2936 lBusNumber = 1;
2937
2938 // remember this ID
2939 idIDEController = ulInstanceID;
2940 lIDEControllerIndex = lIndexThis;
2941 }
2942 break;
2943
2944 case VirtualSystemDescriptionType_HardDiskControllerSATA:
2945 /* <Item>
2946 <rasd:Caption>sataController0</rasd:Caption>
2947 <rasd:Description>SATA Controller</rasd:Description>
2948 <rasd:InstanceId>4</rasd:InstanceId>
2949 <rasd:ResourceType>20</rasd:ResourceType>
2950 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>
2951 <rasd:Address>0</rasd:Address>
2952 <rasd:BusNumber>0</rasd:BusNumber>
2953 </Item>
2954 */
2955 if (uLoop == 1)
2956 {
2957 strDescription = "SATA Controller";
2958 strCaption = "sataController0";
2959 type = OVFResourceType_OtherStorageDevice; // 20
2960 // it seems that OVFTool always writes these two, and since we can only
2961 // have one SATA controller, we'll use this as well
2962 lAddress = 0;
2963 lBusNumber = 0;
2964
2965 if ( desc.strVbox.isEmpty() // AHCI is the default in VirtualBox
2966 || (!desc.strVbox.compare("ahci", Utf8Str::CaseInsensitive))
2967 )
2968 strResourceSubType = "AHCI";
2969 else
2970 throw setError(VBOX_E_NOT_SUPPORTED,
2971 tr("Invalid config string \"%s\" in SATA controller"), desc.strVbox.c_str());
2972
2973 // remember this ID
2974 idSATAController = ulInstanceID;
2975 lSATAControllerIndex = lIndexThis;
2976 }
2977 break;
2978
2979 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2980 /* <Item>
2981 <rasd:Caption>scsiController0</rasd:Caption>
2982 <rasd:Description>SCSI Controller</rasd:Description>
2983 <rasd:InstanceId>4</rasd:InstanceId>
2984 <rasd:ResourceType>6</rasd:ResourceType>
2985 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
2986 <rasd:Address>0</rasd:Address>
2987 <rasd:BusNumber>0</rasd:BusNumber>
2988 </Item>
2989 */
2990 if (uLoop == 1)
2991 {
2992 strDescription = "SCSI Controller";
2993 strCaption = "scsiController0";
2994 type = OVFResourceType_ParallelSCSIHBA; // 6
2995 // it seems that OVFTool always writes these two, and since we can only
2996 // have one SATA controller, we'll use this as well
2997 lAddress = 0;
2998 lBusNumber = 0;
2999
3000 if ( desc.strVbox.isEmpty() // LsiLogic is the default in VirtualBox
3001 || (!desc.strVbox.compare("lsilogic", Utf8Str::CaseInsensitive))
3002 )
3003 strResourceSubType = "lsilogic";
3004 else if (!desc.strVbox.compare("buslogic", Utf8Str::CaseInsensitive))
3005 strResourceSubType = "buslogic";
3006 else
3007 throw setError(VBOX_E_NOT_SUPPORTED,
3008 tr("Invalid config string \"%s\" in SCSI controller"), desc.strVbox.c_str());
3009
3010 // remember this ID
3011 idSCSIController = ulInstanceID;
3012 lSCSIControllerIndex = lIndexThis;
3013 }
3014 break;
3015
3016 case VirtualSystemDescriptionType_HardDiskImage:
3017 /* <Item>
3018 <rasd:Caption>disk1</rasd:Caption>
3019 <rasd:InstanceId>8</rasd:InstanceId>
3020 <rasd:ResourceType>17</rasd:ResourceType>
3021 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
3022 <rasd:Parent>4</rasd:Parent>
3023 <rasd:AddressOnParent>0</rasd:AddressOnParent>
3024 </Item> */
3025 if (uLoop == 2)
3026 {
3027 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
3028
3029 strDescription = "Disk Image";
3030 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else
3031 type = OVFResourceType_HardDisk; // 17
3032
3033 // the following references the "<Disks>" XML block
3034 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
3035
3036 // controller=<index>;channel=<c>
3037 size_t pos1 = desc.strExtraConfig.find("controller=");
3038 size_t pos2 = desc.strExtraConfig.find("channel=");
3039 if (pos1 != Utf8Str::npos)
3040 {
3041 int32_t lControllerIndex = -1;
3042 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
3043 if (lControllerIndex == lIDEControllerIndex)
3044 ulParent = idIDEController;
3045 else if (lControllerIndex == lSCSIControllerIndex)
3046 ulParent = idSCSIController;
3047 else if (lControllerIndex == lSATAControllerIndex)
3048 ulParent = idSATAController;
3049 }
3050 if (pos2 != Utf8Str::npos)
3051 RTStrToInt32Ex(desc.strExtraConfig.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
3052
3053 if ( !ulParent
3054 || lAddressOnParent == -1
3055 )
3056 throw setError(VBOX_E_NOT_SUPPORTED,
3057 tr("Missing or bad extra config string in hard disk image: \"%s\""), desc.strExtraConfig.c_str());
3058
3059 mapDisks[strDiskID] = &desc;
3060 }
3061 break;
3062
3063 case VirtualSystemDescriptionType_Floppy:
3064 if (uLoop == 1)
3065 {
3066 strDescription = "Floppy Drive";
3067 strCaption = "floppy0"; // this is what OVFTool writes
3068 type = OVFResourceType_FloppyDrive; // 14
3069 lAutomaticAllocation = 0;
3070 lAddressOnParent = 0; // this is what OVFTool writes
3071 }
3072 break;
3073
3074 case VirtualSystemDescriptionType_CDROM:
3075 if (uLoop == 2)
3076 {
3077 // we can't have a CD without an IDE controller
3078 if (!idIDEController)
3079 throw setError(VBOX_E_NOT_SUPPORTED,
3080 tr("Can't have CD-ROM without IDE controller"));
3081
3082 strDescription = "CD-ROM Drive";
3083 strCaption = "cdrom1"; // this is what OVFTool writes
3084 type = OVFResourceType_CDDrive; // 15
3085 lAutomaticAllocation = 1;
3086 ulParent = idIDEController;
3087 lAddressOnParent = 0; // this is what OVFTool writes
3088 }
3089 break;
3090
3091 case VirtualSystemDescriptionType_NetworkAdapter:
3092 /* <Item>
3093 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
3094 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
3095 <rasd:Connection>VM Network</rasd:Connection>
3096 <rasd:ElementName>VM network</rasd:ElementName>
3097 <rasd:InstanceID>3</rasd:InstanceID>
3098 <rasd:ResourceType>10</rasd:ResourceType>
3099 </Item> */
3100 if (uLoop == 1)
3101 {
3102 lAutomaticAllocation = 1;
3103 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
3104 type = OVFResourceType_EthernetAdapter; // 10
3105 /* Set the hardware type to something useful.
3106 * To be compatible with vmware & others we set
3107 * PCNet32 for our PCNet types & E1000 for the
3108 * E1000 cards. */
3109 switch (desc.strVbox.toInt32())
3110 {
3111 case NetworkAdapterType_Am79C970A:
3112 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;
3113#ifdef VBOX_WITH_E1000
3114 case NetworkAdapterType_I82540EM:
3115 case NetworkAdapterType_I82545EM:
3116 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;
3117#endif /* VBOX_WITH_E1000 */
3118 }
3119 strConnection = desc.strOvf;
3120
3121 mapNetworks[desc.strOvf] = true;
3122 }
3123 break;
3124
3125 case VirtualSystemDescriptionType_USBController:
3126 /* <Item ovf:required="false">
3127 <rasd:Caption>usb</rasd:Caption>
3128 <rasd:Description>USB Controller</rasd:Description>
3129 <rasd:InstanceId>3</rasd:InstanceId>
3130 <rasd:ResourceType>23</rasd:ResourceType>
3131 <rasd:Address>0</rasd:Address>
3132 <rasd:BusNumber>0</rasd:BusNumber>
3133 </Item> */
3134 if (uLoop == 1)
3135 {
3136 strDescription = "USB Controller";
3137 strCaption = "usb";
3138 type = OVFResourceType_USBController; // 23
3139 lAddress = 0; // this is what OVFTool writes
3140 lBusNumber = 0; // this is what OVFTool writes
3141 }
3142 break;
3143
3144 case VirtualSystemDescriptionType_SoundCard:
3145 /* <Item ovf:required="false">
3146 <rasd:Caption>sound</rasd:Caption>
3147 <rasd:Description>Sound Card</rasd:Description>
3148 <rasd:InstanceId>10</rasd:InstanceId>
3149 <rasd:ResourceType>35</rasd:ResourceType>
3150 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
3151 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
3152 <rasd:AddressOnParent>3</rasd:AddressOnParent>
3153 </Item> */
3154 if (uLoop == 1)
3155 {
3156 strDescription = "Sound Card";
3157 strCaption = "sound";
3158 type = OVFResourceType_SoundCard; // 35
3159 strResourceSubType = desc.strOvf; // e.g. ensoniq1371
3160 lAutomaticAllocation = 0;
3161 lAddressOnParent = 3; // what gives? this is what OVFTool writes
3162 }
3163 break;
3164 }
3165
3166 if (type)
3167 {
3168 xml::ElementNode *pItem;
3169
3170 pItem = pelmVirtualHardwareSection->createChild("Item");
3171
3172 // NOTE: do not change the order of these items without good reason! While we don't care
3173 // about ordering, VMware's ovftool does and fails if the items are not written in
3174 // exactly this order, as stupid as it seems.
3175
3176 if (!strCaption.isEmpty())
3177 pItem->createChild("rasd:Caption")->addContent(strCaption);
3178 if (!strDescription.isEmpty())
3179 pItem->createChild("rasd:Description")->addContent(strDescription);
3180
3181 // <rasd:InstanceID>1</rasd:InstanceID>
3182 xml::ElementNode *pelmInstanceID;
3183 if (task->enFormat == TaskWriteOVF::OVF_0_9)
3184 pelmInstanceID = pItem->createChild("rasd:InstanceId");
3185 else
3186 pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed...
3187 pelmInstanceID->addContent(Utf8StrFmt("%d", ulInstanceID++));
3188
3189 // <rasd:ResourceType>3</rasd:ResourceType>
3190 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
3191 if (!strResourceSubType.isEmpty())
3192 pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
3193
3194 if (!strHostResource.isEmpty())
3195 pItem->createChild("rasd:HostResource")->addContent(strHostResource);
3196
3197 if (!strAllocationUnits.isEmpty())
3198 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
3199
3200 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
3201 if (lVirtualQuantity != -1)
3202 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
3203
3204 if (lAutomaticAllocation != -1)
3205 pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
3206
3207 if (!strConnection.isEmpty())
3208 pItem->createChild("rasd:Connection")->addContent(strConnection);
3209
3210 if (lAddress != -1)
3211 pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
3212
3213 if (lBusNumber != -1)
3214 pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
3215
3216 if (ulParent)
3217 pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
3218 if (lAddressOnParent != -1)
3219 pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
3220 }
3221 }
3222 } // for (size_t uLoop = 0; ...
3223 }
3224
3225 // finally, fill in the network section we set up empty above according
3226 // to the networks we found with the hardware items
3227 map<Utf8Str, bool>::const_iterator itN;
3228 for (itN = mapNetworks.begin();
3229 itN != mapNetworks.end();
3230 ++itN)
3231 {
3232 const Utf8Str &strNetwork = itN->first;
3233 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
3234 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
3235 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
3236 }
3237
3238 map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
3239 uint32_t ulFile = 1;
3240 for (itS = mapDisks.begin();
3241 itS != mapDisks.end();
3242 ++itS)
3243 {
3244 const Utf8Str &strDiskID = itS->first;
3245 const VirtualSystemDescriptionEntry *pDiskEntry = itS->second;
3246
3247 // source path: where the VBox image is
3248 const Utf8Str &strSrcFilePath = pDiskEntry->strVbox;
3249 Bstr bstrSrcFilePath(strSrcFilePath);
3250 if (!RTPathExists(strSrcFilePath.c_str()))
3251 /* This isn't allowed */
3252 throw setError(VBOX_E_FILE_ERROR,
3253 tr("Source virtual disk image file '%s' doesn't exist"),
3254 strSrcFilePath.c_str());
3255
3256 // output filename
3257 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
3258 // target path needs to be composed from where the output OVF is
3259 Utf8Str strTargetFilePath = stripFilename(pAppliance->m->strPath);
3260 strTargetFilePath.append("/");
3261 strTargetFilePath.append(strTargetFileNameOnly);
3262
3263 // clone the disk:
3264 ComPtr<IHardDisk> pSourceDisk;
3265 ComPtr<IHardDisk> pTargetDisk;
3266 ComPtr<IProgress> pProgress2;
3267
3268 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
3269 rc = pVirtualBox->FindHardDisk(bstrSrcFilePath, pSourceDisk.asOutParam());
3270 if (FAILED(rc)) throw rc;
3271
3272 /* We are always exporting to vmdfk stream optimized for now */
3273 Bstr bstrSrcFormat = L"VMDK";
3274
3275 // create a new hard disk interface for the destination disk image
3276 Log(("Creating target disk \"%s\"\n", strTargetFilePath.raw()));
3277 rc = pVirtualBox->CreateHardDisk(bstrSrcFormat, Bstr(strTargetFilePath), pTargetDisk.asOutParam());
3278 if (FAILED(rc)) throw rc;
3279
3280 // the target disk is now registered and needs to be removed again,
3281 // both after successful cloning or if anything goes bad!
3282 try
3283 {
3284 // create a flat copy of the source disk image
3285 rc = pSourceDisk->CloneTo(pTargetDisk, HardDiskVariant_VmdkStreamOptimized, NULL, pProgress2.asOutParam());
3286 if (FAILED(rc)) throw rc;
3287
3288 // advance to the next operation
3289 if (!task->progress.isNull())
3290 task->progress->setNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()),
3291 pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally);
3292
3293 // now wait for the background disk operation to complete; this throws HRESULTs on error
3294 pAppliance->waitForAsyncProgress(task->progress, pProgress2);
3295 }
3296 catch (HRESULT rc3)
3297 {
3298 // upon error after registering, close the disk or
3299 // it'll stick in the registry forever
3300 pTargetDisk->Close();
3301 throw rc3;
3302 }
3303
3304 // we need the following for the XML
3305 uint64_t cbFile = 0; // actual file size
3306 rc = pTargetDisk->COMGETTER(Size)(&cbFile);
3307 if (FAILED(rc)) throw rc;
3308
3309 ULONG64 cbCapacity = 0; // size reported to guest
3310 rc = pTargetDisk->COMGETTER(LogicalSize)(&cbCapacity);
3311 if (FAILED(rc)) throw rc;
3312 // capacity is reported in megabytes, so...
3313 cbCapacity *= _1M;
3314
3315 // upon success, close the disk as well
3316 rc = pTargetDisk->Close();
3317 if (FAILED(rc)) throw rc;
3318
3319 // now handle the XML for the disk:
3320 Utf8StrFmt strFileRef("file%RI32", ulFile++);
3321 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
3322 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
3323 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
3324 pelmFile->setAttribute("ovf:id", strFileRef);
3325 pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());
3326
3327 // add disk to XML Disks section
3328 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="http://www.vmware.com/specifications/vmdk.html#sparse"/>
3329 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
3330 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());
3331 pelmDisk->setAttribute("ovf:diskId", strDiskID);
3332 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
3333 pelmDisk->setAttribute("ovf:format", "http://www.vmware.com/specifications/vmdk.html#sparse"); // must be sparse or ovftool chokes
3334 }
3335
3336 // now go write the XML
3337 xml::XmlFileWriter writer(doc);
3338 writer.write(pAppliance->m->strPath.c_str());
3339 }
3340 catch(xml::Error &x)
3341 {
3342 rc = setError(VBOX_E_FILE_ERROR,
3343 x.what());
3344 }
3345 catch(HRESULT aRC)
3346 {
3347 rc = aRC;
3348 }
3349
3350 task->rc = rc;
3351
3352 if (!task->progress.isNull())
3353 task->progress->notifyComplete(rc);
3354
3355 LogFlowFunc(("rc=%Rhrc\n", rc));
3356 LogFlowFuncLeave();
3357
3358 return VINF_SUCCESS;
3359}
3360
3361/**
3362* Public method implementation.
3363 * @return
3364 */
3365STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
3366{
3367 if (ComSafeArrayOutIsNull(aWarnings))
3368 return E_POINTER;
3369
3370 AutoCaller autoCaller(this);
3371 CheckComRCReturnRC(autoCaller.rc());
3372
3373 AutoReadLock alock(this);
3374
3375 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
3376
3377 list<Utf8Str>::const_iterator it;
3378 size_t i = 0;
3379 for (it = m->llWarnings.begin();
3380 it != m->llWarnings.end();
3381 ++it, ++i)
3382 {
3383 Bstr bstr = *it;
3384 bstr.cloneTo(&sfaWarnings[i]);
3385 }
3386
3387 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
3388
3389 return S_OK;
3390}
3391
3392HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
3393{
3394 IMachine *machine = NULL;
3395 char *tmpName = RTStrDup(aName.c_str());
3396 int i = 1;
3397 /* @todo: Maybe too cost-intensive; try to find a lighter way */
3398 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
3399 {
3400 RTStrFree(tmpName);
3401 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
3402 ++i;
3403 }
3404 aName = tmpName;
3405 RTStrFree(tmpName);
3406
3407 return S_OK;
3408}
3409
3410HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
3411{
3412 IHardDisk *harddisk = NULL;
3413 char *tmpName = RTStrDup(aName.c_str());
3414 int i = 1;
3415 /* Check if the file exists or if a file with this path is registered
3416 * already */
3417 /* @todo: Maybe too cost-intensive; try to find a lighter way */
3418 while (RTPathExists(tmpName) ||
3419 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
3420 {
3421 RTStrFree(tmpName);
3422 char *tmpDir = RTStrDup(aName.c_str());
3423 RTPathStripFilename(tmpDir);;
3424 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
3425 RTPathStripExt(tmpFile);
3426 const char *tmpExt = RTPathExt(aName.c_str());
3427 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
3428 RTStrFree(tmpFile);
3429 RTStrFree(tmpDir);
3430 ++i;
3431 }
3432 aName = tmpName;
3433 RTStrFree(tmpName);
3434
3435 return S_OK;
3436}
3437
3438/**
3439 * Sets up the given progress object so that it represents disk images accurately
3440 * during importMachines() and write().
3441 * @param pProgress
3442 * @param bstrDescription
3443 * @return
3444 */
3445HRESULT Appliance::setUpProgress(ComObjPtr<Progress> &pProgress, const Bstr &bstrDescription)
3446{
3447 HRESULT rc;
3448
3449 /* Create the progress object */
3450 pProgress.createObject();
3451
3452 // weigh the disk images according to their sizes
3453 uint32_t ulTotalMB = 0;
3454 uint32_t cDisks = 0;
3455 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
3456 for (it = m->virtualSystemDescriptions.begin();
3457 it != m->virtualSystemDescriptions.end();
3458 ++it)
3459 {
3460 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
3461 /* One for every hard disk of the Virtual System */
3462 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
3463 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
3464 for (itH = avsdeHDs.begin();
3465 itH != avsdeHDs.end();
3466 ++itH)
3467 {
3468 const VirtualSystemDescriptionEntry *pHD = *itH;
3469 ulTotalMB += pHD->ulSizeMB;
3470 ++cDisks;
3471 }
3472 }
3473
3474 ULONG cOperations = 1 + cDisks; // one op per disk plus 1 for the XML
3475
3476 ULONG ulTotalOperationsWeight;
3477 if (ulTotalMB)
3478 {
3479 m->ulWeightPerOperation = (ULONG)((double)ulTotalMB * 1 / 100); // use 1% of the progress for the XML
3480 ulTotalOperationsWeight = ulTotalMB + m->ulWeightPerOperation;
3481 }
3482 else
3483 {
3484 // no disks to export:
3485 ulTotalOperationsWeight = 1;
3486 m->ulWeightPerOperation = 1;
3487 }
3488
3489 Log(("Setting up progress object: ulTotalMB = %d, cDisks = %d, => cOperations = %d, ulTotalOperationsWeight = %d, m->ulWeightPerOperation = %d\n",
3490 ulTotalMB, cDisks, cOperations, ulTotalOperationsWeight, m->ulWeightPerOperation));
3491
3492 rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
3493 bstrDescription,
3494 TRUE /* aCancelable */,
3495 cOperations, // ULONG cOperations,
3496 ulTotalOperationsWeight, // ULONG ulTotalOperationsWeight,
3497 bstrDescription, // CBSTR bstrFirstOperationDescription,
3498 m->ulWeightPerOperation); // ULONG ulFirstOperationWeight,
3499 return rc;
3500}
3501
3502/**
3503 * Called from the import and export background threads to synchronize the second
3504 * background disk thread's progress object with the current progress object so
3505 * that the user interface sees progress correctly and that cancel signals are
3506 * passed on to the second thread.
3507 * @param pProgressThis Progress object of the current thread.
3508 * @param pProgressAsync Progress object of asynchronous task running in background.
3509 */
3510void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis,
3511 ComPtr<IProgress> &pProgressAsync)
3512{
3513 HRESULT rc;
3514
3515 // now loop until the asynchronous operation completes and then report its result
3516 BOOL fCompleted;
3517 BOOL fCanceled;
3518 ULONG currentPercent;
3519 while (SUCCEEDED(pProgressAsync->COMGETTER(Completed(&fCompleted))))
3520 {
3521 rc = pProgressThis->COMGETTER(Canceled)(&fCanceled);
3522 if (FAILED(rc)) throw rc;
3523 if (fCanceled)
3524 {
3525 pProgressAsync->Cancel();
3526 break;
3527 }
3528
3529 rc = pProgressAsync->COMGETTER(Percent(&currentPercent));
3530 if (FAILED(rc)) throw rc;
3531 if (!pProgressThis.isNull())
3532 pProgressThis->setCurrentOperationProgress(currentPercent);
3533 if (fCompleted)
3534 break;
3535
3536 /* Make sure the loop is not too tight */
3537 rc = pProgressAsync->WaitForCompletion(100);
3538 if (FAILED(rc)) throw rc;
3539 }
3540 // report result of asynchronous operation
3541 HRESULT vrc;
3542 rc = pProgressAsync->COMGETTER(ResultCode)(&vrc);
3543 if (FAILED(rc)) throw rc;
3544
3545
3546 // if the thread of the progress object has an error, then
3547 // retrieve the error info from there, or it'll be lost
3548 if (FAILED(vrc))
3549 {
3550 ProgressErrorInfo info(pProgressAsync);
3551 Utf8Str str(info.getText());
3552 const char *pcsz = str.c_str();
3553 HRESULT rc2 = setError(vrc, pcsz);
3554 throw rc2;
3555 }
3556}
3557
3558void Appliance::addWarning(const char* aWarning, ...)
3559{
3560 va_list args;
3561 va_start(args, aWarning);
3562 Utf8StrFmtVA str(aWarning, args);
3563 va_end(args);
3564 m->llWarnings.push_back(str);
3565}
3566
3567////////////////////////////////////////////////////////////////////////////////
3568//
3569// IVirtualSystemDescription constructor / destructor
3570//
3571////////////////////////////////////////////////////////////////////////////////
3572
3573DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
3574struct shutup3 {};
3575
3576/**
3577 * COM initializer.
3578 * @return
3579 */
3580HRESULT VirtualSystemDescription::init()
3581{
3582 /* Enclose the state transition NotReady->InInit->Ready */
3583 AutoInitSpan autoInitSpan(this);
3584 AssertReturn(autoInitSpan.isOk(), E_FAIL);
3585
3586 /* Initialize data */
3587 m = new Data();
3588
3589 /* Confirm a successful initialization */
3590 autoInitSpan.setSucceeded();
3591 return S_OK;
3592}
3593
3594/**
3595* COM uninitializer.
3596*/
3597
3598void VirtualSystemDescription::uninit()
3599{
3600 delete m;
3601 m = NULL;
3602}
3603
3604////////////////////////////////////////////////////////////////////////////////
3605//
3606// IVirtualSystemDescription public methods
3607//
3608////////////////////////////////////////////////////////////////////////////////
3609
3610/**
3611 * Public method implementation.
3612 * @param
3613 * @return
3614 */
3615STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
3616{
3617 if (!aCount)
3618 return E_POINTER;
3619
3620 AutoCaller autoCaller(this);
3621 CheckComRCReturnRC(autoCaller.rc());
3622
3623 AutoReadLock alock(this);
3624
3625 *aCount = (ULONG)m->llDescriptions.size();
3626
3627 return S_OK;
3628}
3629
3630/**
3631 * Public method implementation.
3632 * @return
3633 */
3634STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3635 ComSafeArrayOut(BSTR, aRefs),
3636 ComSafeArrayOut(BSTR, aOrigValues),
3637 ComSafeArrayOut(BSTR, aVboxValues),
3638 ComSafeArrayOut(BSTR, aExtraConfigValues))
3639{
3640 if (ComSafeArrayOutIsNull(aTypes) ||
3641 ComSafeArrayOutIsNull(aRefs) ||
3642 ComSafeArrayOutIsNull(aOrigValues) ||
3643 ComSafeArrayOutIsNull(aVboxValues) ||
3644 ComSafeArrayOutIsNull(aExtraConfigValues))
3645 return E_POINTER;
3646
3647 AutoCaller autoCaller(this);
3648 CheckComRCReturnRC(autoCaller.rc());
3649
3650 AutoReadLock alock(this);
3651
3652 ULONG c = (ULONG)m->llDescriptions.size();
3653 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3654 com::SafeArray<BSTR> sfaRefs(c);
3655 com::SafeArray<BSTR> sfaOrigValues(c);
3656 com::SafeArray<BSTR> sfaVboxValues(c);
3657 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3658
3659 list<VirtualSystemDescriptionEntry>::const_iterator it;
3660 size_t i = 0;
3661 for (it = m->llDescriptions.begin();
3662 it != m->llDescriptions.end();
3663 ++it, ++i)
3664 {
3665 const VirtualSystemDescriptionEntry &vsde = (*it);
3666
3667 sfaTypes[i] = vsde.type;
3668
3669 Bstr bstr = vsde.strRef;
3670 bstr.cloneTo(&sfaRefs[i]);
3671
3672 bstr = vsde.strOvf;
3673 bstr.cloneTo(&sfaOrigValues[i]);
3674
3675 bstr = vsde.strVbox;
3676 bstr.cloneTo(&sfaVboxValues[i]);
3677
3678 bstr = vsde.strExtraConfig;
3679 bstr.cloneTo(&sfaExtraConfigValues[i]);
3680 }
3681
3682 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3683 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3684 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3685 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3686 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3687
3688 return S_OK;
3689}
3690
3691/**
3692 * Public method implementation.
3693 * @return
3694 */
3695STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescriptionType_T aType,
3696 ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
3697 ComSafeArrayOut(BSTR, aRefs),
3698 ComSafeArrayOut(BSTR, aOrigValues),
3699 ComSafeArrayOut(BSTR, aVboxValues),
3700 ComSafeArrayOut(BSTR, aExtraConfigValues))
3701{
3702 if (ComSafeArrayOutIsNull(aTypes) ||
3703 ComSafeArrayOutIsNull(aRefs) ||
3704 ComSafeArrayOutIsNull(aOrigValues) ||
3705 ComSafeArrayOutIsNull(aVboxValues) ||
3706 ComSafeArrayOutIsNull(aExtraConfigValues))
3707 return E_POINTER;
3708
3709 AutoCaller autoCaller(this);
3710 CheckComRCReturnRC(autoCaller.rc());
3711
3712 AutoReadLock alock(this);
3713
3714 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
3715 ULONG c = (ULONG)vsd.size();
3716 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
3717 com::SafeArray<BSTR> sfaRefs(c);
3718 com::SafeArray<BSTR> sfaOrigValues(c);
3719 com::SafeArray<BSTR> sfaVboxValues(c);
3720 com::SafeArray<BSTR> sfaExtraConfigValues(c);
3721
3722 list<VirtualSystemDescriptionEntry*>::const_iterator it;
3723 size_t i = 0;
3724 for (it = vsd.begin();
3725 it != vsd.end();
3726 ++it, ++i)
3727 {
3728 const VirtualSystemDescriptionEntry *vsde = (*it);
3729
3730 sfaTypes[i] = vsde->type;
3731
3732 Bstr bstr = vsde->strRef;
3733 bstr.cloneTo(&sfaRefs[i]);
3734
3735 bstr = vsde->strOvf;
3736 bstr.cloneTo(&sfaOrigValues[i]);
3737
3738 bstr = vsde->strVbox;
3739 bstr.cloneTo(&sfaVboxValues[i]);
3740
3741 bstr = vsde->strExtraConfig;
3742 bstr.cloneTo(&sfaExtraConfigValues[i]);
3743 }
3744
3745 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
3746 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
3747 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
3748 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
3749 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
3750
3751 return S_OK;
3752}
3753
3754/**
3755 * Public method implementation.
3756 * @return
3757 */
3758STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionType_T aType,
3759 VirtualSystemDescriptionValueType_T aWhich,
3760 ComSafeArrayOut(BSTR, aValues))
3761{
3762 if (ComSafeArrayOutIsNull(aValues))
3763 return E_POINTER;
3764
3765 AutoCaller autoCaller(this);
3766 CheckComRCReturnRC(autoCaller.rc());
3767
3768 AutoReadLock alock(this);
3769
3770 std::list<VirtualSystemDescriptionEntry*> vsd = findByType (aType);
3771 com::SafeArray<BSTR> sfaValues((ULONG)vsd.size());
3772
3773 list<VirtualSystemDescriptionEntry*>::const_iterator it;
3774 size_t i = 0;
3775 for (it = vsd.begin();
3776 it != vsd.end();
3777 ++it, ++i)
3778 {
3779 const VirtualSystemDescriptionEntry *vsde = (*it);
3780
3781 Bstr bstr;
3782 switch (aWhich)
3783 {
3784 case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break;
3785 case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break;
3786 case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVbox; break;
3787 case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfig; break;
3788 }
3789
3790 bstr.cloneTo(&sfaValues[i]);
3791 }
3792
3793 sfaValues.detachTo(ComSafeArrayOutArg(aValues));
3794
3795 return S_OK;
3796}
3797
3798/**
3799 * Public method implementation.
3800 * @return
3801 */
3802STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
3803 ComSafeArrayIn(IN_BSTR, argVboxValues),
3804 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
3805{
3806#ifndef RT_OS_WINDOWS
3807 NOREF(aEnabledSize);
3808#endif /* RT_OS_WINDOWS */
3809
3810 CheckComArgSafeArrayNotNull(aEnabled);
3811 CheckComArgSafeArrayNotNull(argVboxValues);
3812 CheckComArgSafeArrayNotNull(argExtraConfigValues);
3813
3814 AutoCaller autoCaller(this);
3815 CheckComRCReturnRC(autoCaller.rc());
3816
3817 AutoWriteLock alock(this);
3818
3819 com::SafeArray<BOOL> sfaEnabled(ComSafeArrayInArg(aEnabled));
3820 com::SafeArray<IN_BSTR> sfaVboxValues(ComSafeArrayInArg(argVboxValues));
3821 com::SafeArray<IN_BSTR> sfaExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
3822
3823 if ( (sfaEnabled.size() != m->llDescriptions.size())
3824 || (sfaVboxValues.size() != m->llDescriptions.size())
3825 || (sfaExtraConfigValues.size() != m->llDescriptions.size())
3826 )
3827 return E_INVALIDARG;
3828
3829 list<VirtualSystemDescriptionEntry>::iterator it;
3830 size_t i = 0;
3831 for (it = m->llDescriptions.begin();
3832 it != m->llDescriptions.end();
3833 ++it, ++i)
3834 {
3835 VirtualSystemDescriptionEntry& vsde = *it;
3836
3837 if (sfaEnabled[i])
3838 {
3839 vsde.strVbox = sfaVboxValues[i];
3840 vsde.strExtraConfig = sfaExtraConfigValues[i];
3841 }
3842 else
3843 vsde.type = VirtualSystemDescriptionType_Ignore;
3844 }
3845
3846 return S_OK;
3847}
3848
3849/**
3850 * Public method implementation.
3851 * @return
3852 */
3853STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType,
3854 IN_BSTR aVboxValue,
3855 IN_BSTR aExtraConfigValue)
3856{
3857 CheckComArgNotNull(aVboxValue);
3858 CheckComArgNotNull(aExtraConfigValue);
3859
3860 AutoCaller autoCaller(this);
3861 CheckComRCReturnRC(autoCaller.rc());
3862
3863 AutoWriteLock alock(this);
3864
3865 addEntry(aType, "", aVboxValue, aVboxValue, 0, aExtraConfigValue);
3866
3867 return S_OK;
3868}
3869
3870/**
3871 * Internal method; adds a new description item to the member list.
3872 * @param aType Type of description for the new item.
3873 * @param strRef Reference item; only used with hard disk controllers.
3874 * @param aOrigValue Corresponding original value from OVF.
3875 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
3876 * @param strExtraConfig Extra configuration; meaning dependent on type.
3877 */
3878void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
3879 const Utf8Str &strRef,
3880 const Utf8Str &aOrigValue,
3881 const Utf8Str &aAutoValue,
3882 uint32_t ulSizeMB,
3883 const Utf8Str &strExtraConfig /*= ""*/)
3884{
3885 VirtualSystemDescriptionEntry vsde;
3886 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
3887 vsde.type = aType;
3888 vsde.strRef = strRef;
3889 vsde.strOvf = aOrigValue;
3890 vsde.strVbox = aAutoValue;
3891 vsde.strExtraConfig = strExtraConfig;
3892 vsde.ulSizeMB = ulSizeMB;
3893
3894 m->llDescriptions.push_back(vsde);
3895}
3896
3897/**
3898 * Private method; returns a list of description items containing all the items from the member
3899 * description items of this virtual system that match the given type.
3900 * @param aType
3901 * @return
3902 */
3903std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
3904{
3905 std::list<VirtualSystemDescriptionEntry*> vsd;
3906
3907 list<VirtualSystemDescriptionEntry>::iterator it;
3908 for (it = m->llDescriptions.begin();
3909 it != m->llDescriptions.end();
3910 ++it)
3911 {
3912 if (it->type == aType)
3913 vsd.push_back(&(*it));
3914 }
3915
3916 return vsd;
3917}
3918
3919/**
3920 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
3921 * the given reference ID. Useful when needing the controller for a particular
3922 * virtual disk.
3923 * @param id
3924 * @return
3925 */
3926const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
3927{
3928 Utf8Str strRef = Utf8StrFmt("%RI32", id);
3929 list<VirtualSystemDescriptionEntry>::const_iterator it;
3930 for (it = m->llDescriptions.begin();
3931 it != m->llDescriptions.end();
3932 ++it)
3933 {
3934 const VirtualSystemDescriptionEntry &d = *it;
3935 switch (d.type)
3936 {
3937 case VirtualSystemDescriptionType_HardDiskControllerIDE:
3938 case VirtualSystemDescriptionType_HardDiskControllerSATA:
3939 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
3940 if (d.strRef == strRef)
3941 return &d;
3942 break;
3943 }
3944 }
3945
3946 return NULL;
3947}
3948
3949////////////////////////////////////////////////////////////////////////////////
3950//
3951// IMachine public methods
3952//
3953////////////////////////////////////////////////////////////////////////////////
3954
3955// This code is here so we won't have to include the appliance headers in the
3956// IMachine implementation, and we also need to access private appliance data.
3957
3958/**
3959* Public method implementation.
3960* @param appliance
3961* @return
3962*/
3963
3964STDMETHODIMP Machine::Export(IAppliance *aAppliance, IVirtualSystemDescription **aDescription)
3965{
3966 HRESULT rc = S_OK;
3967
3968 if (!aAppliance)
3969 return E_POINTER;
3970
3971 AutoCaller autoCaller(this);
3972 CheckComRCReturnRC(autoCaller.rc());
3973
3974 AutoReadLock alock(this);
3975
3976 ComObjPtr<VirtualSystemDescription> pNewDesc;
3977
3978 try
3979 {
3980 Bstr bstrName;
3981 Bstr bstrDescription;
3982 Bstr bstrGuestOSType;
3983 uint32_t cCPUs;
3984 uint32_t ulMemSizeMB;
3985 BOOL fDVDEnabled;
3986 BOOL fFloppyEnabled;
3987 BOOL fUSBEnabled;
3988 BOOL fAudioEnabled;
3989 AudioControllerType_T audioController;
3990
3991 ComPtr<IUSBController> pUsbController;
3992 ComPtr<IAudioAdapter> pAudioAdapter;
3993
3994 // get name
3995 bstrName = mUserData->mName;
3996 // get description
3997 bstrDescription = mUserData->mDescription;
3998 // get guest OS
3999 bstrGuestOSType = mUserData->mOSTypeId;
4000 // CPU count
4001 cCPUs = mHWData->mCPUCount;
4002 // memory size in MB
4003 ulMemSizeMB = mHWData->mMemorySize;
4004 // VRAM size?
4005 // BIOS settings?
4006 // 3D acceleration enabled?
4007 // hardware virtualization enabled?
4008 // nested paging enabled?
4009 // HWVirtExVPIDEnabled?
4010 // PAEEnabled?
4011 // snapshotFolder?
4012 // VRDPServer?
4013
4014 // floppy
4015 rc = mFloppyDrive->COMGETTER(Enabled)(&fFloppyEnabled);
4016 if (FAILED(rc)) throw rc;
4017
4018 // CD-ROM ?!?
4019 // ComPtr<IDVDDrive> pDVDDrive;
4020 fDVDEnabled = 1;
4021
4022 // this is more tricky so use the COM method
4023 rc = COMGETTER(USBController)(pUsbController.asOutParam());
4024 if (FAILED(rc)) throw rc;
4025 rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
4026
4027 pAudioAdapter = mAudioAdapter;
4028 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
4029 if (FAILED(rc)) throw rc;
4030 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
4031 if (FAILED(rc)) throw rc;
4032
4033 // create a new virtual system
4034 rc = pNewDesc.createObject();
4035 CheckComRCThrowRC(rc);
4036 rc = pNewDesc->init();
4037 CheckComRCThrowRC(rc);
4038
4039 /* Guest OS type */
4040 Utf8Str strOsTypeVBox(bstrGuestOSType);
4041 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
4042 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
4043 "",
4044 Utf8StrFmt("%RI32", cim),
4045 strOsTypeVBox);
4046
4047 /* VM name */
4048 Utf8Str strVMName(bstrName);
4049 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
4050 "",
4051 strVMName,
4052 strVMName);
4053
4054 // description
4055 Utf8Str strDescription(bstrDescription);
4056 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
4057 "",
4058 strDescription,
4059 strDescription);
4060
4061 /* CPU count*/
4062 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
4063 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
4064 "",
4065 strCpuCount,
4066 strCpuCount);
4067
4068 /* Memory */
4069 Utf8Str strMemory = Utf8StrFmt("%RI32", (uint64_t)ulMemSizeMB * _1M);
4070 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
4071 "",
4072 strMemory,
4073 strMemory);
4074
4075 int32_t lIDEControllerIndex = 0;
4076 int32_t lSATAControllerIndex = 0;
4077 int32_t lSCSIControllerIndex = 0;
4078
4079// <const name="HardDiskControllerIDE" value="6" />
4080 ComPtr<IStorageController> pController;
4081 rc = GetStorageControllerByName(Bstr("IDE"), pController.asOutParam());
4082 if (FAILED(rc)) throw rc;
4083 Utf8Str strVbox;
4084 StorageControllerType_T ctlr;
4085 rc = pController->COMGETTER(ControllerType)(&ctlr);
4086 if (FAILED(rc)) throw rc;
4087 switch(ctlr)
4088 {
4089 case StorageControllerType_PIIX3: strVbox = "PIIX3"; break;
4090 case StorageControllerType_PIIX4: strVbox = "PIIX4"; break;
4091 case StorageControllerType_ICH6: strVbox = "ICH6"; break;
4092 }
4093
4094 if (strVbox.length())
4095 {
4096 lIDEControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
4097 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
4098 Utf8StrFmt("%d", lIDEControllerIndex),
4099 strVbox,
4100 strVbox);
4101 }
4102
4103#ifdef VBOX_WITH_AHCI
4104// <const name="HardDiskControllerSATA" value="7" />
4105 rc = GetStorageControllerByName(Bstr("SATA"), pController.asOutParam());
4106 if (SUCCEEDED(rc))
4107 {
4108 strVbox = "AHCI";
4109 lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
4110 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
4111 Utf8StrFmt("%d", lSATAControllerIndex),
4112 strVbox,
4113 strVbox);
4114 }
4115#endif // VBOX_WITH_AHCI
4116
4117// <const name="HardDiskControllerSCSI" value="8" />
4118 rc = GetStorageControllerByName(Bstr("SCSI"), pController.asOutParam());
4119 if (SUCCEEDED(rc))
4120 {
4121 rc = pController->COMGETTER(ControllerType)(&ctlr);
4122 if (SUCCEEDED(rc))
4123 {
4124 strVbox = "LsiLogic"; // the default in VBox
4125 switch(ctlr)
4126 {
4127 case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break;
4128 case StorageControllerType_BusLogic: strVbox = "BusLogic"; break;
4129 }
4130 lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size();
4131 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
4132 Utf8StrFmt("%d", lSCSIControllerIndex),
4133 strVbox,
4134 strVbox);
4135 }
4136 else
4137 throw rc;
4138 }
4139
4140// <const name="HardDiskImage" value="9" />
4141 HDData::AttachmentList::iterator itA;
4142 for (itA = mHDData->mAttachments.begin();
4143 itA != mHDData->mAttachments.end();
4144 ++itA)
4145 {
4146 ComObjPtr<HardDiskAttachment> pHDA = *itA;
4147
4148 // the attachment's data
4149 ComPtr<IHardDisk> pHardDisk;
4150 ComPtr<IStorageController> ctl;
4151 Bstr controllerName;
4152
4153 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
4154 if (FAILED(rc)) throw rc;
4155
4156 rc = GetStorageControllerByName(controllerName, ctl.asOutParam());
4157 if (FAILED(rc)) throw rc;
4158
4159 StorageBus_T storageBus;
4160 LONG lChannel;
4161 LONG lDevice;
4162
4163 rc = ctl->COMGETTER(Bus)(&storageBus);
4164 if (FAILED(rc)) throw rc;
4165
4166 rc = pHDA->COMGETTER(HardDisk)(pHardDisk.asOutParam());
4167 if (FAILED(rc)) throw rc;
4168
4169 rc = pHDA->COMGETTER(Port)(&lChannel);
4170 if (FAILED(rc)) throw rc;
4171
4172 rc = pHDA->COMGETTER(Device)(&lDevice);
4173 if (FAILED(rc)) throw rc;
4174
4175 Bstr bstrLocation;
4176 rc = pHardDisk->COMGETTER(Location)(bstrLocation.asOutParam());
4177 if (FAILED(rc)) throw rc;
4178 Bstr bstrName;
4179 rc = pHardDisk->COMGETTER(Name)(bstrName.asOutParam());
4180 if (FAILED(rc)) throw rc;
4181
4182 // force reading state, or else size will be returned as 0
4183 MediaState_T ms;
4184 rc = pHardDisk->COMGETTER(State)(&ms);
4185 if (FAILED(rc)) throw rc;
4186
4187 ULONG64 ullSize;
4188 rc = pHardDisk->COMGETTER(Size)(&ullSize);
4189 if (FAILED(rc)) throw rc;
4190
4191 // and how this translates to the virtual system
4192 int32_t lControllerVsys = 0;
4193 LONG lChannelVsys;
4194
4195 switch (storageBus)
4196 {
4197 case StorageBus_IDE:
4198 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
4199 // and it must be updated when that is changed!
4200
4201 if (lChannel == 0 && lDevice == 0) // primary master
4202 lChannelVsys = 0;
4203 else if (lChannel == 0 && lDevice == 1) // primary slave
4204 lChannelVsys = 1;
4205 else if (lChannel == 1 && lDevice == 1) // secondary slave; secondary master is always CDROM
4206 lChannelVsys = 2;
4207 else
4208 throw setError(VBOX_E_NOT_SUPPORTED,
4209 tr("Cannot handle hard disk attachment: channel is %d, device is %d"), lChannel, lDevice);
4210
4211 lControllerVsys = lIDEControllerIndex;
4212 break;
4213
4214 case StorageBus_SATA:
4215 lChannelVsys = lChannel; // should be between 0 and 29
4216 lControllerVsys = lSATAControllerIndex;
4217 break;
4218
4219 case StorageBus_SCSI:
4220 lChannelVsys = lChannel; // should be between 0 and 15
4221 lControllerVsys = lSCSIControllerIndex;
4222 break;
4223
4224 default:
4225 throw setError(VBOX_E_NOT_SUPPORTED,
4226 tr("Cannot handle hard disk attachment: storageBus is %d, channel is %d, device is %d"), storageBus, lChannel, lDevice);
4227 break;
4228 }
4229
4230 Utf8Str strTargetVmdkName(bstrName);
4231 RTPathStripExt(strTargetVmdkName.mutableRaw());
4232 strTargetVmdkName.append(".vmdk");
4233
4234 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
4235 strTargetVmdkName, // disk ID: let's use the name
4236 strTargetVmdkName, // OVF value:
4237 Utf8Str(bstrLocation), // vbox value: media path
4238 (uint32_t)(ullSize / _1M),
4239 Utf8StrFmt("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys));
4240 }
4241
4242 /* Floppy Drive */
4243 if (fFloppyEnabled)
4244 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
4245
4246 /* CD Drive */
4247 if (fDVDEnabled)
4248 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
4249
4250// <const name="NetworkAdapter" />
4251 size_t a;
4252 for (a = 0;
4253 a < SchemaDefs::NetworkAdapterCount;
4254 ++a)
4255 {
4256 ComPtr<INetworkAdapter> pNetworkAdapter;
4257 BOOL fEnabled;
4258 NetworkAdapterType_T adapterType;
4259 NetworkAttachmentType_T attachmentType;
4260
4261 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
4262 if (FAILED(rc)) throw rc;
4263 /* Enable the network card & set the adapter type */
4264 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
4265 if (FAILED(rc)) throw rc;
4266
4267 if (fEnabled)
4268 {
4269 Utf8Str strAttachmentType;
4270
4271 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
4272 if (FAILED(rc)) throw rc;
4273
4274 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
4275 if (FAILED(rc)) throw rc;
4276
4277 switch (attachmentType)
4278 {
4279 case NetworkAttachmentType_Null:
4280 strAttachmentType = "Null";
4281 break;
4282
4283 case NetworkAttachmentType_NAT:
4284 strAttachmentType = "NAT";
4285 break;
4286
4287 case NetworkAttachmentType_Bridged:
4288 strAttachmentType = "Bridged";
4289 break;
4290
4291 case NetworkAttachmentType_Internal:
4292 strAttachmentType = "Internal";
4293 break;
4294
4295 case NetworkAttachmentType_HostOnly:
4296 strAttachmentType = "HostOnly";
4297 break;
4298 }
4299
4300 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
4301 "", // ref
4302 strAttachmentType, // orig
4303 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
4304 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
4305 }
4306 }
4307
4308// <const name="USBController" />
4309#ifdef VBOX_WITH_USB
4310 if (fUSBEnabled)
4311 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
4312#endif /* VBOX_WITH_USB */
4313
4314// <const name="SoundCard" />
4315 if (fAudioEnabled)
4316 {
4317 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
4318 "",
4319 "ensoniq1371", // this is what OVFTool writes and VMware supports
4320 Utf8StrFmt("%RI32", audioController));
4321 }
4322
4323 // finally, add the virtual system to the appliance
4324 Appliance *pAppliance = static_cast<Appliance*>(aAppliance);
4325 AutoCaller autoCaller1(pAppliance);
4326 CheckComRCReturnRC(autoCaller1.rc());
4327
4328 /* We return the new description to the caller */
4329 ComPtr<IVirtualSystemDescription> copy(pNewDesc);
4330 copy.queryInterfaceTo(aDescription);
4331
4332 AutoWriteLock alock(pAppliance);
4333
4334 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
4335 }
4336 catch(HRESULT arc)
4337 {
4338 rc = arc;
4339 }
4340
4341 return rc;
4342}
4343
4344/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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