VirtualBox

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

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

OVF: export os type

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 134.9 KB
Line 
1/* $Id: ApplianceImpl.cpp 17580 2009-03-09 14:49:09Z 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 <iprt/stream.h>
24#include <iprt/path.h>
25#include <iprt/dir.h>
26#include <iprt/file.h>
27
28#include "ApplianceImpl.h"
29#include "VirtualBoxImpl.h"
30#include "GuestOSTypeImpl.h"
31#include "ProgressImpl.h"
32#include "MachineImpl.h"
33
34#include "Logging.h"
35
36#include "VBox/xml.h"
37
38using namespace std;
39
40////////////////////////////////////////////////////////////////////////////////
41//
42// hardware definitions
43//
44////////////////////////////////////////////////////////////////////////////////
45
46struct DiskImage
47{
48 Utf8Str strDiskId; // value from DiskSection/Disk/@diskId
49 int64_t iCapacity; // value from DiskSection/Disk/@capacity;
50 // (maximum size for dynamic images, I guess; we always translate this to bytes)
51 int64_t iPopulatedSize; // value from DiskSection/Disk/@populatedSize
52 // (actual used size of disk, always in bytes; can be an estimate of used disk
53 // space, but cannot be larger than iCapacity)
54 Utf8Str strFormat; // value from DiskSection/Disk/@format
55 // typically http://www.vmware.com/specifications/vmdk.html#sparse
56
57 // fields from /References/File; the spec says the file reference from disk can be empty,
58 // so in that case, strFilename will be empty, then a new disk should be created
59 Utf8Str strHref; // value from /References/File/@href (filename); if empty, then the remaining fields are ignored
60 int64_t iSize; // value from /References/File/@size (optional according to spec; then we set -1 here)
61 int64_t iChunkSize; // value from /References/File/@chunkSize (optional, unsupported)
62 Utf8Str strCompression; // value from /References/File/@compression (optional, can be "gzip" according to spec)
63};
64
65struct VirtualHardwareItem
66{
67 Utf8Str strDescription;
68 Utf8Str strCaption;
69 Utf8Str strElementName;
70
71 uint32_t ulInstanceID;
72 uint32_t ulParent;
73
74 OVFResourceType_T resourceType;
75 Utf8Str strOtherResourceType;
76 Utf8Str strResourceSubType;
77
78 Utf8Str strHostResource; // "Abstractly specifies how a device shall connect to a resource on the deployment platform.
79 // Not all devices need a backing." Used with disk items, for which this references a virtual
80 // disk from the Disks section.
81 bool fAutomaticAllocation;
82 bool fAutomaticDeallocation;
83 Utf8Str strConnection; // "All Ethernet adapters that specify the same abstract network connection name within an OVF
84 // package shall be deployed on the same network. The abstract network connection name shall be
85 // listed in the NetworkSection at the outermost envelope level." We ignore this and only set up
86 // a network adapter depending on the network name.
87 Utf8Str strAddress; // "Device-specific. For an Ethernet adapter, this specifies the MAC address."
88 Utf8Str strAddressOnParent; // "For a device, this specifies its location on the controller."
89 Utf8Str strAllocationUnits; // "Specifies the units of allocation used. For example, “byte * 2^20”."
90 uint64_t ullVirtualQuantity; // "Specifies the quantity of resources presented. For example, “256”."
91 uint64_t ullReservation; // "Specifies the minimum quantity of resources guaranteed to be available."
92 uint64_t ullLimit; // "Specifies the maximum quantity of resources that will be granted."
93 uint64_t ullWeight; // "Specifies a relative priority for this allocation in relation to other allocations."
94
95 Utf8Str strConsumerVisibility;
96 Utf8Str strMappingBehavior;
97 Utf8Str strPoolID;
98 uint32_t ulBusNumber; // seen with IDE controllers, but not listed in OVF spec
99
100 uint32_t ulLineNumber; // line number of <Item> element in XML source; cached for error messages
101
102 VirtualHardwareItem()
103 : ulInstanceID(0), fAutomaticAllocation(false), fAutomaticDeallocation(false), ullVirtualQuantity(0), ullReservation(0), ullLimit(0), ullWeight(0), ulBusNumber(0), ulLineNumber(0)
104 {};
105};
106
107typedef map<Utf8Str, DiskImage> DiskImagesMap;
108
109struct VirtualSystem;
110
111typedef map<uint32_t, VirtualHardwareItem> HardwareItemsMap;
112
113struct HardDiskController
114{
115 uint32_t idController; // instance ID (Item/InstanceId); this gets referenced from HardDisk
116 enum ControllerSystemType { IDE, SATA, SCSI };
117 ControllerSystemType system; // one of IDE, SATA, SCSI
118 Utf8Str strControllerType; // controller subtype (Item/ResourceSubType); e.g. "LsiLogic"; can be empty (esp. for IDE)
119 Utf8Str strAddress; // for IDE
120 uint32_t ulBusNumber; // for IDE
121
122 HardDiskController()
123 : idController(0),
124 ulBusNumber(0)
125 {
126 }
127};
128
129typedef map<uint32_t, HardDiskController> ControllersMap;
130
131struct VirtualDisk
132{
133 uint32_t idController; // SCSI (or IDE) controller this disk is connected to;
134 // points into VirtualSystem.mapControllers
135 uint32_t ulAddressOnParent; // parsed strAddressOnParent of hardware item; will be 0 or 1 for IDE
136 // and possibly higher for disks attached to SCSI controllers (untested)
137 Utf8Str strDiskId; // if the hard disk has an ovf:/disk/<id> reference,
138 // this receives the <id> component; points to one of the
139 // references in Appliance::Data.mapDisks
140};
141
142typedef map<Utf8Str, VirtualDisk> VirtualDisksMap;
143
144struct VirtualSystem
145{
146 Utf8Str strName; // copy of VirtualSystem/@id
147
148 CIMOSType_T cimos;
149 Utf8Str strVirtualSystemType; // generic hardware description; OVF says this can be something like "vmx-4" or "xen";
150 // VMware Workstation 6.5 is "vmx-07"
151
152 HardwareItemsMap mapHardwareItems; // map of virtual hardware items, sorted by unique instance ID
153
154 uint64_t ullMemorySize; // always in bytes, copied from llHardwareItems; default = 0 (unspecified)
155 uint16_t cCPUs; // no. of CPUs, copied from llHardwareItems; default = 1
156
157 list<Utf8Str> llNetworkNames;
158 // list of strings referring to network names
159 // (one for each VirtualSystem/Item[@ResourceType=10]/Connection element)
160
161 ControllersMap mapControllers;
162 // list of hard disk controllers
163 // (one for each VirtualSystem/Item[@ResourceType=6] element with accumulated data from children)
164
165 VirtualDisksMap mapVirtualDisks;
166 // (one for each VirtualSystem/Item[@ResourceType=17] element with accumulated data from children)
167
168 bool fHasFloppyDrive; // true if there's a floppy item in mapHardwareItems
169 bool fHasCdromDrive; // true if there's a CD-ROM item in mapHardwareItems; ISO images are not yet supported by OVFtool
170 bool fHasUsbController; // true if there's a USB controller item in mapHardwareItems
171
172 Utf8Str strSoundCardType; // if not empty, then the system wants a soundcard; this then specifies the hardware;
173 // VMware Workstation 6.5 uses "ensoniq1371" for example
174
175 Utf8Str strLicenceInfo; // license info if any; receives contents of VirtualSystem/EulaSection/Info
176 Utf8Str strLicenceText; // license info if any; receives contents of VirtualSystem/EulaSection/License
177
178 VirtualSystem()
179 : ullMemorySize(0), cCPUs(1), fHasFloppyDrive(false), fHasCdromDrive(false), fHasUsbController(false)
180 {
181 }
182};
183
184////////////////////////////////////////////////////////////////////////////////
185//
186// Appliance data definition
187//
188////////////////////////////////////////////////////////////////////////////////
189
190// opaque private instance data of Appliance class
191struct Appliance::Data
192{
193 Utf8Str strPath; // file name last given to either read() or write()
194
195 DiskImagesMap mapDisks; // map of DiskImage structs, sorted by DiskImage.strDiskId
196
197 list<VirtualSystem> llVirtualSystems; // list of virtual systems, created by and valid after read()
198
199 list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions; //
200};
201
202struct VirtualSystemDescription::Data
203{
204 list<VirtualSystemDescriptionEntry> llDescriptions;
205 list<Utf8Str> llWarnings;
206};
207
208////////////////////////////////////////////////////////////////////////////////
209//
210// Threads
211//
212////////////////////////////////////////////////////////////////////////////////
213
214struct Appliance::TaskImportMachines
215{
216 TaskImportMachines(Appliance *aThat, Progress *aProgress)
217 : pAppliance(aThat)
218 , progress(aProgress)
219 , rc(S_OK)
220 {}
221 ~TaskImportMachines() {}
222
223 HRESULT startThread();
224
225 Appliance *pAppliance;
226 ComObjPtr<Progress> progress;
227 HRESULT rc;
228};
229
230struct Appliance::TaskExportOVF
231{
232 TaskExportOVF(Appliance *aThat, Progress *aProgress)
233 : pAppliance(aThat)
234 , progress(aProgress)
235 , rc(S_OK)
236 {}
237 ~TaskExportOVF() {}
238
239 HRESULT startThread();
240
241 Appliance *pAppliance;
242 ComObjPtr<Progress> progress;
243 HRESULT rc;
244};
245
246////////////////////////////////////////////////////////////////////////////////
247//
248// internal helpers
249//
250////////////////////////////////////////////////////////////////////////////////
251
252static Utf8Str stripFilename(const Utf8Str &strFile)
253{
254 Utf8Str str2(strFile);
255 RTPathStripFilename(str2.mutableRaw());
256 return str2;
257}
258
259static const struct
260{
261 CIMOSType_T cim;
262 const char *pcszVbox;
263}
264 g_osTypes[] =
265 {
266 { CIMOSType_CIMOS_Unknown, SchemaDefs_OSTypeId_Other },
267 { CIMOSType_CIMOS_OS2, SchemaDefs_OSTypeId_OS2 },
268 { CIMOSType_CIMOS_MSDOS, SchemaDefs_OSTypeId_DOS },
269 { CIMOSType_CIMOS_WIN3x, SchemaDefs_OSTypeId_Windows31 },
270 { CIMOSType_CIMOS_WIN95, SchemaDefs_OSTypeId_Windows95 },
271 { CIMOSType_CIMOS_WIN98, SchemaDefs_OSTypeId_Windows98 },
272 { CIMOSType_CIMOS_WINNT, SchemaDefs_OSTypeId_WindowsNT4 },
273 { CIMOSType_CIMOS_NetWare, SchemaDefs_OSTypeId_Netware },
274 { CIMOSType_CIMOS_NovellOES, SchemaDefs_OSTypeId_Netware },
275 { CIMOSType_CIMOS_Solaris, SchemaDefs_OSTypeId_Solaris },
276 { CIMOSType_CIMOS_SunOS, SchemaDefs_OSTypeId_Solaris },
277 { CIMOSType_CIMOS_FreeBSD, SchemaDefs_OSTypeId_FreeBSD },
278 { CIMOSType_CIMOS_NetBSD, SchemaDefs_OSTypeId_NetBSD },
279 { CIMOSType_CIMOS_QNX, SchemaDefs_OSTypeId_QNX },
280 { CIMOSType_CIMOS_Windows2000, SchemaDefs_OSTypeId_Windows2000 },
281 { CIMOSType_CIMOS_WindowsMe, SchemaDefs_OSTypeId_WindowsMe },
282 { CIMOSType_CIMOS_OpenBSD, SchemaDefs_OSTypeId_OpenBSD },
283 { CIMOSType_CIMOS_WindowsXP, SchemaDefs_OSTypeId_WindowsXP },
284 { CIMOSType_CIMOS_WindowsXPEmbedded, SchemaDefs_OSTypeId_WindowsXP },
285 { CIMOSType_CIMOS_WindowsEmbeddedforPointofService, SchemaDefs_OSTypeId_WindowsXP },
286 { CIMOSType_CIMOS_MicrosoftWindowsServer2003, SchemaDefs_OSTypeId_Windows2003 },
287 { CIMOSType_CIMOS_MicrosoftWindowsServer2003_64, SchemaDefs_OSTypeId_Windows2003_64 },
288 { CIMOSType_CIMOS_WindowsXP_64, SchemaDefs_OSTypeId_WindowsXP_64 },
289 { CIMOSType_CIMOS_WindowsVista, SchemaDefs_OSTypeId_WindowsVista },
290 { CIMOSType_CIMOS_WindowsVista_64, SchemaDefs_OSTypeId_WindowsVista_64 },
291 { CIMOSType_CIMOS_MicrosoftWindowsServer2008, SchemaDefs_OSTypeId_Windows2008 },
292 { CIMOSType_CIMOS_MicrosoftWindowsServer2008_64, SchemaDefs_OSTypeId_Windows2008_64 },
293 { CIMOSType_CIMOS_FreeBSD_64, SchemaDefs_OSTypeId_FreeBSD_64 },
294 { CIMOSType_CIMOS_RedHatEnterpriseLinux, SchemaDefs_OSTypeId_RedHat },
295 { CIMOSType_CIMOS_RedHatEnterpriseLinux_64, SchemaDefs_OSTypeId_RedHat_64 },
296 { CIMOSType_CIMOS_Solaris_64, SchemaDefs_OSTypeId_Solaris_64 },
297 { CIMOSType_CIMOS_SUSE, SchemaDefs_OSTypeId_OpenSUSE },
298 { CIMOSType_CIMOS_SLES, SchemaDefs_OSTypeId_OpenSUSE },
299 { CIMOSType_CIMOS_NovellLinuxDesktop, SchemaDefs_OSTypeId_OpenSUSE },
300 { CIMOSType_CIMOS_SUSE_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
301 { CIMOSType_CIMOS_SLES_64, SchemaDefs_OSTypeId_OpenSUSE_64 },
302 { CIMOSType_CIMOS_LINUX, SchemaDefs_OSTypeId_Linux },
303 { CIMOSType_CIMOS_SunJavaDesktopSystem, SchemaDefs_OSTypeId_Linux },
304 { CIMOSType_CIMOS_TurboLinux, SchemaDefs_OSTypeId_Linux},
305
306 // { CIMOSType_CIMOS_TurboLinux_64, },
307 // { CIMOSType_CIMOS_Linux_64, },
308 // osTypeVBox = VBOXOSTYPE_Linux_x64;
309 // break;
310
311 { CIMOSType_CIMOS_Mandriva, SchemaDefs_OSTypeId_Mandriva },
312 { CIMOSType_CIMOS_Mandriva_64, SchemaDefs_OSTypeId_Mandriva_64 },
313 { CIMOSType_CIMOS_Ubuntu, SchemaDefs_OSTypeId_Ubuntu },
314 { CIMOSType_CIMOS_Ubuntu_64, SchemaDefs_OSTypeId_Ubuntu_64 },
315 { CIMOSType_CIMOS_Debian, SchemaDefs_OSTypeId_Debian },
316 { CIMOSType_CIMOS_Debian_64, SchemaDefs_OSTypeId_Debian_64 },
317 { CIMOSType_CIMOS_Linux_2_4_x, SchemaDefs_OSTypeId_Linux24 },
318 { CIMOSType_CIMOS_Linux_2_4_x_64, SchemaDefs_OSTypeId_Linux24_64 },
319 { CIMOSType_CIMOS_Linux_2_6_x, SchemaDefs_OSTypeId_Linux26 },
320 { CIMOSType_CIMOS_Linux_2_6_x_64, SchemaDefs_OSTypeId_Linux26_64 }
321};
322
323/**
324 * Private helper func that suggests a VirtualBox guest OS type
325 * for the given OVF operating system type.
326 * @param osTypeVBox
327 * @param c
328 */
329static void convertCIMOSType2VBoxOSType(Utf8Str &strType, CIMOSType_T c)
330{
331 const char *osTypeVBox = "";
332
333 for (size_t i = 0;
334 i < RT_ELEMENTS(g_osTypes);
335 ++i)
336 {
337 if (c == g_osTypes[i].cim)
338 {
339 strType = g_osTypes[i].pcszVbox;
340 return;
341 }
342 }
343
344 strType = SchemaDefs_OSTypeId_Other;
345}
346
347/**
348 * Private helper func that suggests a VirtualBox guest OS type
349 * for the given OVF operating system type.
350 * @param osTypeVBox
351 * @param c
352 */
353static CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox)
354{
355 const char *osTypeVBox = "";
356
357 for (size_t i = 0;
358 i < RT_ELEMENTS(g_osTypes);
359 ++i)
360 {
361 if (!RTStrICmp(pcszVbox, g_osTypes[i].pcszVbox))
362 return g_osTypes[i].cim;
363 }
364
365 return CIMOSType_CIMOS_Other;
366}
367
368////////////////////////////////////////////////////////////////////////////////
369//
370// IVirtualBox public methods
371//
372////////////////////////////////////////////////////////////////////////////////
373
374// This code is here so we won't have to include the appliance headers in the
375// IVirtualBox implementation.
376
377/**
378 * Implementation for IVirtualBox::createAppliance.
379 *
380 * @param anAppliance IAppliance object created if S_OK is returned.
381 * @return S_OK or error.
382 */
383STDMETHODIMP VirtualBox::CreateAppliance(IAppliance** anAppliance)
384{
385 HRESULT rc;
386
387 ComObjPtr<Appliance> appliance;
388 appliance.createObject();
389 rc = appliance->init(this);
390
391 if (SUCCEEDED(rc))
392 appliance.queryInterfaceTo(anAppliance);
393
394 return rc;
395}
396
397////////////////////////////////////////////////////////////////////////////////
398//
399// Appliance::task methods
400//
401////////////////////////////////////////////////////////////////////////////////
402
403HRESULT Appliance::TaskImportMachines::startThread()
404{
405 int vrc = RTThreadCreate(NULL, Appliance::taskThreadImportMachines, this,
406 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
407 "Appliance::Task");
408 ComAssertMsgRCRet(vrc,
409 ("Could not create taskThreadImportMachines (%Rrc)\n", vrc), E_FAIL);
410
411 return S_OK;
412}
413
414HRESULT Appliance::TaskExportOVF::startThread()
415{
416 int vrc = RTThreadCreate(NULL, Appliance::taskThreadExportOVF, this,
417 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
418 "Appliance::Task");
419 ComAssertMsgRCRet(vrc,
420 ("Could not create taskThreadExportOVF (%Rrc)\n", vrc), E_FAIL);
421
422 return S_OK;
423}
424
425////////////////////////////////////////////////////////////////////////////////
426//
427// Appliance constructor / destructor
428//
429////////////////////////////////////////////////////////////////////////////////
430
431DEFINE_EMPTY_CTOR_DTOR(Appliance)
432struct shutup {};
433
434/**
435 * Appliance COM initializer.
436 * @param
437 * @return
438 */
439
440HRESULT Appliance::init(VirtualBox *aVirtualBox)
441{
442 /* Enclose the state transition NotReady->InInit->Ready */
443 AutoInitSpan autoInitSpan(this);
444 AssertReturn(autoInitSpan.isOk(), E_FAIL);
445
446 /* Weak reference to a VirtualBox object */
447 unconst(mVirtualBox) = aVirtualBox;
448
449 // initialize data
450 m = new Data;
451
452 /* Confirm a successful initialization */
453 autoInitSpan.setSucceeded();
454
455 return S_OK;
456}
457
458/**
459 * Appliance COM uninitializer.
460 * @return
461 */
462void Appliance::uninit()
463{
464 delete m;
465 m = NULL;
466}
467
468////////////////////////////////////////////////////////////////////////////////
469//
470// Appliance private methods
471//
472////////////////////////////////////////////////////////////////////////////////
473
474/**
475 * Private helper method that goes thru the elements of the given "current" element in the OVF XML
476 * and handles the contained child elements (which can be "Section" or "Content" elements).
477 *
478 * @param pcszPath Path spec of the XML file, for error messages.
479 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
480 * @param pCurElem Element whose children are to be analyzed here.
481 * @return
482 */
483HRESULT Appliance::LoopThruSections(const char *pcszPath,
484 const xml::ElementNode *pReferencesElem,
485 const xml::ElementNode *pCurElem)
486{
487 HRESULT rc;
488
489 xml::NodesLoop loopChildren(*pCurElem);
490 const xml::ElementNode *pElem;
491 while ((pElem = loopChildren.forAllNodes()))
492 {
493 const char *pcszElemName = pElem->getName();
494 const char *pcszTypeAttr = "";
495 const xml::AttributeNode *pTypeAttr;
496 if ((pTypeAttr = pElem->findAttribute("type")))
497 pcszTypeAttr = pTypeAttr->getValue();
498
499 if ( (!strcmp(pcszElemName, "DiskSection"))
500 || ( (!strcmp(pcszElemName, "Section"))
501 && (!strcmp(pcszTypeAttr, "ovf:DiskSection_Type"))
502 )
503 )
504 {
505 if (!(SUCCEEDED((rc = HandleDiskSection(pcszPath, pReferencesElem, pElem)))))
506 return rc;
507 }
508 else if ( (!strcmp(pcszElemName, "NetworkSection")) // we ignore NetworkSections for now
509 || ( (!strcmp(pcszElemName, "Section"))
510 && (!strcmp(pcszTypeAttr, "ovf:NetworkSection_Type"))
511 )
512 )
513 {
514 if (!(SUCCEEDED((rc = HandleNetworkSection(pcszPath, pElem)))))
515 return rc;
516 }
517 else if ( (!strcmp(pcszElemName, "DeploymentOptionSection>")))
518 {
519 // TODO
520 }
521 else if ( (!strcmp(pcszElemName, "Info")))
522 {
523 // child of VirtualSystemCollection -- TODO
524 }
525 else if ( (!strcmp(pcszElemName, "ResourceAllocationSection")))
526 {
527 // child of VirtualSystemCollection -- TODO
528 }
529 else if ( (!strcmp(pcszElemName, "StartupSection")))
530 {
531 // child of VirtualSystemCollection -- TODO
532 }
533 else if ( (!strcmp(pcszElemName, "VirtualSystem"))
534 || ( (!strcmp(pcszElemName, "Content"))
535 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystem_Type"))
536 )
537 )
538 {
539 if (!(SUCCEEDED((rc = HandleVirtualSystemContent(pcszPath, pElem)))))
540 return rc;
541 }
542 else if ( (!strcmp(pcszElemName, "VirtualSystemCollection"))
543 || ( (!strcmp(pcszElemName, "Content"))
544 && (!strcmp(pcszTypeAttr, "ovf:VirtualSystemCollection_Type"))
545 )
546 )
547 {
548 // TODO ResourceAllocationSection
549
550 // recurse for this, since it has VirtualSystem elements as children
551 if (!(SUCCEEDED((rc = LoopThruSections(pcszPath, pReferencesElem, pElem)))))
552 return rc;
553 }
554 }
555
556 return S_OK;
557}
558
559/**
560 * Private helper method that handles disk sections in the OVF XML.
561 * Gets called indirectly from IAppliance::read().
562 *
563 * @param pcszPath Path spec of the XML file, for error messages.
564 * @param pReferencesElement "References" element from OVF, for looking up file specifications; can be NULL if no such element is present.
565 * @param pSectionElem Section element for which this helper is getting called.
566 * @return
567 */
568HRESULT Appliance::HandleDiskSection(const char *pcszPath,
569 const xml::ElementNode *pReferencesElem,
570 const xml::ElementNode *pSectionElem)
571{
572 // contains "Disk" child elements
573 xml::NodesLoop loopDisks(*pSectionElem, "Disk");
574 const xml::ElementNode *pelmDisk;
575 while ((pelmDisk = loopDisks.forAllNodes()))
576 {
577 DiskImage d;
578 const char *pcszBad = NULL;
579 if (!(pelmDisk->getAttributeValue("diskId", d.strDiskId)))
580 pcszBad = "diskId";
581 else if (!(pelmDisk->getAttributeValue("format", d.strFormat)))
582 pcszBad = "format";
583 else if (!(pelmDisk->getAttributeValue("capacity", d.iCapacity)))
584 pcszBad = "capacity";
585 else
586 {
587 if (!(pelmDisk->getAttributeValue("populatedSize", d.iPopulatedSize)))
588 // optional
589 d.iPopulatedSize = -1;
590
591 Utf8Str strFileRef;
592 if (pelmDisk->getAttributeValue("fileRef", strFileRef)) // optional
593 {
594 // look up corresponding /References/File nodes (list built above)
595 const xml::ElementNode *pFileElem;
596 if ( pReferencesElem
597 && ((pFileElem = pReferencesElem->findChildElementFromId(strFileRef.c_str())))
598 )
599 {
600 // copy remaining values from file node then
601 const char *pcszBadInFile = NULL;
602 if (!(pFileElem->getAttributeValue("href", d.strHref)))
603 pcszBadInFile = "href";
604 else if (!(pFileElem->getAttributeValue("size", d.iSize)))
605 d.iSize = -1; // optional
606 // if (!(pFileElem->getAttributeValue("size", d.iChunkSize))) TODO
607 d.iChunkSize = -1; // optional
608 pFileElem->getAttributeValue("compression", d.strCompression);
609
610 if (pcszBadInFile)
611 return setError(VBOX_E_FILE_ERROR,
612 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'File' element, line %d"),
613 pcszPath,
614 pcszBadInFile,
615 pFileElem->getLineNumber());
616 }
617 else
618 return setError(VBOX_E_FILE_ERROR,
619 tr("Error reading \"%s\": cannot find References/File element for ID '%s' referenced by 'Disk' element, line %d"),
620 pcszPath,
621 strFileRef.c_str(),
622 pelmDisk->getLineNumber());
623 }
624 }
625
626 if (pcszBad)
627 return setError(VBOX_E_FILE_ERROR,
628 tr("Error reading \"%s\": missing or invalid attribute '%s' in 'DiskSection' element, line %d"),
629 pcszPath,
630 pcszBad,
631 pelmDisk->getLineNumber());
632
633 m->mapDisks[d.strDiskId] = d;
634 }
635
636 return S_OK;
637}
638
639/**
640 * Private helper method that handles network sections in the OVF XML.
641 * Gets called indirectly from IAppliance::read().
642 *
643 * @param pcszPath Path spec of the XML file, for error messages.
644 * @param pSectionElem Section element for which this helper is getting called.
645 * @return
646 */
647HRESULT Appliance::HandleNetworkSection(const char *pcszPath,
648 const xml::ElementNode *pSectionElem)
649{
650 // we ignore network sections for now
651
652// xml::NodesLoop loopNetworks(*pSectionElem, "Network");
653// const xml::Node *pelmNetwork;
654// while ((pelmNetwork = loopNetworks.forAllNodes()))
655// {
656// Network n;
657// if (!(pelmNetwork->getAttributeValue("name", n.strNetworkName)))
658// return setError(VBOX_E_FILE_ERROR,
659// tr("Error reading \"%s\": missing 'name' attribute in 'Network', line %d"),
660// pcszPath,
661// pelmNetwork->getLineNumber());
662//
663// m->mapNetworks[n.strNetworkName] = n;
664// }
665
666 return S_OK;
667}
668
669/**
670 * Private helper method that handles a "VirtualSystem" element in the OVF XML.
671 * Gets called indirectly from IAppliance::read().
672 *
673 * @param pcszPath
674 * @param pContentElem
675 * @return
676 */
677HRESULT Appliance::HandleVirtualSystemContent(const char *pcszPath,
678 const xml::ElementNode *pelmVirtualSystem)
679{
680 VirtualSystem vsys;
681
682 const xml::AttributeNode *pIdAttr = pelmVirtualSystem->findAttribute("id");
683 if (pIdAttr)
684 vsys.strName = pIdAttr->getValue();
685
686 xml::NodesLoop loop(*pelmVirtualSystem); // all child elements
687 const xml::ElementNode *pelmThis;
688 while ((pelmThis = loop.forAllNodes()))
689 {
690 const char *pcszElemName = pelmThis->getName();
691 const xml::AttributeNode *pTypeAttr = pelmThis->findAttribute("type");
692 const char *pcszTypeAttr = (pTypeAttr) ? pTypeAttr->getValue() : "";
693
694 if (!strcmp(pcszElemName, "EulaSection"))
695 {
696 /* <EulaSection>
697 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
698 <License ovf:msgid="1">License terms can go in here.</License>
699 </EulaSection> */
700
701 const xml::ElementNode *pelmInfo, *pelmLicense;
702 if ( ((pelmInfo = pelmThis->findChildElement("Info")))
703 && ((pelmLicense = pelmThis->findChildElement("License")))
704 )
705 {
706 vsys.strLicenceInfo = pelmInfo->getValue();
707 vsys.strLicenceText = pelmLicense->getValue();
708 }
709 }
710 else if ( (!strcmp(pcszElemName, "VirtualHardwareSection"))
711 || (!strcmp(pcszTypeAttr, "ovf:VirtualHardwareSection_Type"))
712 )
713 {
714 const xml::ElementNode *pelmSystem, *pelmVirtualSystemType;
715 if ((pelmSystem = pelmThis->findChildElement("System")))
716 {
717 /* <System>
718 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
719 <vssd:ElementName>vmware</vssd:ElementName>
720 <vssd:InstanceID>1</vssd:InstanceID>
721 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
722 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
723 </System>*/
724 if ((pelmVirtualSystemType = pelmSystem->findChildElement("VirtualSystemType")))
725 vsys.strVirtualSystemType = pelmVirtualSystemType->getValue();
726 }
727
728 xml::NodesLoop loopVirtualHardwareItems(*pelmThis, "Item"); // all "Item" child elements
729 const xml::ElementNode *pelmItem;
730 while ((pelmItem = loopVirtualHardwareItems.forAllNodes()))
731 {
732 VirtualHardwareItem i;
733
734 i.ulLineNumber = pelmItem->getLineNumber();
735
736 xml::NodesLoop loopItemChildren(*pelmItem); // all child elements
737 const xml::ElementNode *pelmItemChild;
738 while ((pelmItemChild = loopItemChildren.forAllNodes()))
739 {
740 const char *pcszItemChildName = pelmItemChild->getName();
741 if (!strcmp(pcszItemChildName, "Description"))
742 i.strDescription = pelmItemChild->getValue();
743 else if (!strcmp(pcszItemChildName, "Caption"))
744 i.strCaption = pelmItemChild->getValue();
745 else if (!strcmp(pcszItemChildName, "ElementName"))
746 i.strElementName = pelmItemChild->getValue();
747 else if ( (!strcmp(pcszItemChildName, "InstanceID"))
748 || (!strcmp(pcszItemChildName, "InstanceId"))
749 )
750 pelmItemChild->copyValue(i.ulInstanceID);
751 else if (!strcmp(pcszItemChildName, "HostResource"))
752 i.strHostResource = pelmItemChild->getValue();
753 else if (!strcmp(pcszItemChildName, "ResourceType"))
754 {
755 int32_t iType; /** @todo how to fix correctly? (enum fun.) */
756 pelmItemChild->copyValue(iType);
757 i.resourceType = (OVFResourceType_T)iType;
758 }
759 else if (!strcmp(pcszItemChildName, "OtherResourceType"))
760 i.strOtherResourceType = pelmItemChild->getValue();
761 else if (!strcmp(pcszItemChildName, "ResourceSubType"))
762 i.strResourceSubType = pelmItemChild->getValue();
763 else if (!strcmp(pcszItemChildName, "AutomaticAllocation"))
764 i.fAutomaticAllocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
765 else if (!strcmp(pcszItemChildName, "AutomaticDeallocation"))
766 i.fAutomaticDeallocation = (!strcmp(pelmItemChild->getValue(), "true")) ? true : false;
767 else if (!strcmp(pcszItemChildName, "Parent"))
768 pelmItemChild->copyValue(i.ulParent);
769 else if (!strcmp(pcszItemChildName, "Connection"))
770 i.strConnection = pelmItemChild->getValue();
771 else if (!strcmp(pcszItemChildName, "Address"))
772 i.strAddress = pelmItemChild->getValue();
773 else if (!strcmp(pcszItemChildName, "AddressOnParent"))
774 i.strAddressOnParent = pelmItemChild->getValue();
775 else if (!strcmp(pcszItemChildName, "AllocationUnits"))
776 i.strAllocationUnits = pelmItemChild->getValue();
777 else if (!strcmp(pcszItemChildName, "VirtualQuantity"))
778 pelmItemChild->copyValue(i.ullVirtualQuantity);
779 else if (!strcmp(pcszItemChildName, "Reservation"))
780 pelmItemChild->copyValue(i.ullReservation);
781 else if (!strcmp(pcszItemChildName, "Limit"))
782 pelmItemChild->copyValue(i.ullLimit);
783 else if (!strcmp(pcszItemChildName, "Weight"))
784 pelmItemChild->copyValue(i.ullWeight);
785 else if (!strcmp(pcszItemChildName, "ConsumerVisibility"))
786 i.strConsumerVisibility = pelmItemChild->getValue();
787 else if (!strcmp(pcszItemChildName, "MappingBehavior"))
788 i.strMappingBehavior = pelmItemChild->getValue();
789 else if (!strcmp(pcszItemChildName, "PoolID"))
790 i.strPoolID = pelmItemChild->getValue();
791 else if (!strcmp(pcszItemChildName, "BusNumber"))
792 pelmItemChild->copyValue(i.ulBusNumber);
793 else
794 return setError(VBOX_E_FILE_ERROR,
795 tr("Error reading \"%s\": unknown element \"%s\" under Item element, line %d"),
796 pcszPath,
797 pcszItemChildName,
798 i.ulLineNumber);
799 }
800
801 // store!
802 vsys.mapHardwareItems[i.ulInstanceID] = i;
803 }
804
805 HardwareItemsMap::const_iterator itH;
806
807 for (itH = vsys.mapHardwareItems.begin();
808 itH != vsys.mapHardwareItems.end();
809 ++itH)
810 {
811 const VirtualHardwareItem &i = itH->second;
812
813 // do some analysis
814 switch (i.resourceType)
815 {
816 case OVFResourceType_Processor: // 3
817 /* <rasd:Caption>1 virtual CPU</rasd:Caption>
818 <rasd:Description>Number of virtual CPUs</rasd:Description>
819 <rasd:ElementName>virtual CPU</rasd:ElementName>
820 <rasd:InstanceID>1</rasd:InstanceID>
821 <rasd:ResourceType>3</rasd:ResourceType>
822 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>*/
823 if (i.ullVirtualQuantity < UINT16_MAX)
824 vsys.cCPUs = (uint16_t)i.ullVirtualQuantity;
825 else
826 return setError(VBOX_E_FILE_ERROR,
827 tr("Error reading \"%s\": CPU count %RI64 is larger than %d, line %d"),
828 pcszPath,
829 i.ullVirtualQuantity,
830 UINT16_MAX,
831 i.ulLineNumber);
832 break;
833
834 case OVFResourceType_Memory: // 4
835 if ( (i.strAllocationUnits == "MegaBytes") // found in OVF created by OVF toolkit
836 || (i.strAllocationUnits == "MB") // found in MS docs
837 || (i.strAllocationUnits == "byte * 2^20") // suggested by OVF spec DSP0243 page 21
838 )
839 vsys.ullMemorySize = i.ullVirtualQuantity * 1024 * 1024;
840 else
841 return setError(VBOX_E_FILE_ERROR,
842 tr("Error reading \"%s\": Invalid allocation unit \"%s\" specified with memory size item, line %d"),
843 pcszPath,
844 i.strAllocationUnits.c_str(),
845 i.ulLineNumber);
846 break;
847
848 case OVFResourceType_IdeController: // 5 IdeController
849 {
850 /* <Item>
851 <rasd:Caption>ideController0</rasd:Caption>
852 <rasd:Description>IDE Controller</rasd:Description>
853 <rasd:InstanceId>5</rasd:InstanceId>
854 <rasd:ResourceType>5</rasd:ResourceType>
855 <rasd:Address>0</rasd:Address>
856 <rasd:BusNumber>0</rasd:BusNumber>
857 </Item> */
858 HardDiskController hdc;
859 hdc.system = HardDiskController::IDE;
860 hdc.idController = i.ulInstanceID;
861 hdc.strAddress = i.strAddress;
862 hdc.ulBusNumber = i.ulBusNumber;
863
864 vsys.mapControllers[i.ulInstanceID] = hdc;
865 }
866 break;
867
868 case OVFResourceType_ParallelScsiHba: // 6 SCSI controller
869 {
870 /* <Item>
871 <rasd:Caption>SCSI Controller 0 - LSI Logic</rasd:Caption>
872 <rasd:Description>SCI Controller</rasd:Description>
873 <rasd:ElementName>SCSI controller</rasd:ElementName>
874 <rasd:InstanceID>4</rasd:InstanceID>
875 <rasd:ResourceSubType>LsiLogic</rasd:ResourceSubType>
876 <rasd:ResourceType>6</rasd:ResourceType>
877 </Item> */
878 HardDiskController hdc;
879 hdc.system = HardDiskController::SCSI;
880 hdc.idController = i.ulInstanceID;
881 hdc.strControllerType = i.strResourceSubType;
882
883 vsys.mapControllers[i.ulInstanceID] = hdc;
884 }
885 break;
886
887 case OVFResourceType_EthernetAdapter: // 10
888 {
889 /* <Item>
890 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
891 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
892 <rasd:Connection>VM Network</rasd:Connection>
893 <rasd:Description>VM Network?</rasd:Description>
894 <rasd:ElementName>Ethernet adapter</rasd:ElementName>
895 <rasd:InstanceID>3</rasd:InstanceID>
896 <rasd:ResourceType>10</rasd:ResourceType>
897 </Item>
898
899 OVF spec DSP 0243 page 21:
900 "For an Ethernet adapter, this specifies the abstract network connection name
901 for the virtual machine. All Ethernet adapters that specify the same abstract
902 network connection name within an OVF package shall be deployed on the same
903 network. The abstract network connection name shall be listed in the NetworkSection
904 at the outermost envelope level." */
905
906 // only store the name
907 vsys.llNetworkNames.push_back(i.strConnection);
908 }
909 break;
910
911 case OVFResourceType_FloppyDrive: // 14
912 vsys.fHasFloppyDrive = true; // we have no additional information
913 break;
914
915 case OVFResourceType_CdDrive: // 15
916 /* <Item ovf:required="false">
917 <rasd:Caption>cdrom1</rasd:Caption>
918 <rasd:InstanceId>7</rasd:InstanceId>
919 <rasd:ResourceType>15</rasd:ResourceType>
920 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
921 <rasd:Parent>5</rasd:Parent>
922 <rasd:AddressOnParent>0</rasd:AddressOnParent>
923 </Item> */
924 // I tried to see what happens if I set an ISO for the CD-ROM in VMware Workstation,
925 // but then the ovftool dies with "Device backing not supported". So I guess if
926 // VMware can't export ISOs, then we don't need to be able to import them right now.
927 vsys.fHasCdromDrive = true; // we have no additional information
928 break;
929
930 case OVFResourceType_HardDisk: // 17
931 {
932 /* <Item>
933 <rasd:Caption>Harddisk 1</rasd:Caption>
934 <rasd:Description>HD</rasd:Description>
935 <rasd:ElementName>Hard Disk</rasd:ElementName>
936 <rasd:HostResource>ovf://disk/lamp</rasd:HostResource>
937 <rasd:InstanceID>5</rasd:InstanceID>
938 <rasd:Parent>4</rasd:Parent>
939 <rasd:ResourceType>17</rasd:ResourceType>
940 </Item> */
941
942 // look up the hard disk controller element whose InstanceID equals our Parent;
943 // this is how the connection is specified in OVF
944 ControllersMap::const_iterator it = vsys.mapControllers.find(i.ulParent);
945 if (it == vsys.mapControllers.end())
946 return setError(VBOX_E_FILE_ERROR,
947 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid parent %d, line %d"),
948 pcszPath,
949 i.ulInstanceID,
950 i.ulParent,
951 i.ulLineNumber);
952 const HardDiskController &hdc = it->second;
953
954 VirtualDisk vd;
955 vd.idController = i.ulParent;
956 i.strAddressOnParent.toInt(vd.ulAddressOnParent);
957 bool fFound = false;
958 // ovf://disk/lamp
959 // 12345678901234
960 if (i.strHostResource.substr(0, 11) == "ovf://disk/")
961 vd.strDiskId = i.strHostResource.substr(11);
962 else if (i.strHostResource.substr(0, 6) == "/disk/")
963 vd.strDiskId = i.strHostResource.substr(6);
964
965 if ( !(vd.strDiskId.length())
966 || (m->mapDisks.find(vd.strDiskId) == m->mapDisks.end())
967 )
968 return setError(VBOX_E_FILE_ERROR,
969 tr("Error reading \"%s\": Hard disk item with instance ID %d specifies invalid host resource \"%s\", line %d"),
970 pcszPath,
971 i.ulInstanceID,
972 i.strHostResource.c_str(),
973 i.ulLineNumber);
974
975 vsys.mapVirtualDisks[vd.strDiskId] = vd;
976 }
977 break;
978
979 case OVFResourceType_UsbController: // 23
980 /* <Item ovf:required="false">
981 <rasd:Caption>usb</rasd:Caption>
982 <rasd:Description>USB Controller</rasd:Description>
983 <rasd:InstanceId>3</rasd:InstanceId>
984 <rasd:ResourceType>23</rasd:ResourceType>
985 <rasd:Address>0</rasd:Address>
986 <rasd:BusNumber>0</rasd:BusNumber>
987 </Item> */
988 vsys.fHasUsbController = true; // we have no additional information
989 break;
990
991 case OVFResourceType_SoundCard: // 35
992 /* <Item ovf:required="false">
993 <rasd:Caption>sound</rasd:Caption>
994 <rasd:Description>Sound Card</rasd:Description>
995 <rasd:InstanceId>10</rasd:InstanceId>
996 <rasd:ResourceType>35</rasd:ResourceType>
997 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
998 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
999 <rasd:AddressOnParent>3</rasd:AddressOnParent>
1000 </Item> */
1001 vsys.strSoundCardType = i.strResourceSubType;
1002 break;
1003
1004 default:
1005 return setError(VBOX_E_FILE_ERROR,
1006 tr("Error reading \"%s\": Unknown resource type %d in hardware item, line %d"),
1007 pcszPath,
1008 i.resourceType,
1009 i.ulLineNumber);
1010 }
1011 }
1012 }
1013 else if ( (!strcmp(pcszElemName, "OperatingSystemSection"))
1014 || (!strcmp(pcszTypeAttr, "ovf:OperatingSystemSection_Type"))
1015 )
1016 {
1017 uint64_t cimos64;
1018 if (!(pelmThis->getAttributeValue("id", cimos64)))
1019 return setError(VBOX_E_FILE_ERROR,
1020 tr("Error reading \"%s\": missing or invalid 'ovf:id' attribute in operating system section element, line %d"),
1021 pcszPath,
1022 pelmThis->getLineNumber());
1023
1024 vsys.cimos = (CIMOSType_T)cimos64;
1025 }
1026 }
1027
1028 // now create the virtual system
1029 m->llVirtualSystems.push_back(vsys);
1030
1031 return S_OK;
1032}
1033
1034////////////////////////////////////////////////////////////////////////////////
1035//
1036// IAppliance public methods
1037//
1038////////////////////////////////////////////////////////////////////////////////
1039
1040/**
1041 * Public method implementation.
1042 * @param
1043 * @return
1044 */
1045STDMETHODIMP Appliance::COMGETTER(Path)(BSTR *aPath)
1046{
1047 if (!aPath)
1048 return E_POINTER;
1049
1050 AutoCaller autoCaller(this);
1051 CheckComRCReturnRC(autoCaller.rc());
1052
1053 AutoReadLock alock(this);
1054
1055 Bstr bstrPath(m->strPath);
1056 bstrPath.cloneTo(aPath);
1057
1058 return S_OK;
1059}
1060
1061/**
1062 * Public method implementation.
1063 * @param
1064 * @return
1065 */
1066STDMETHODIMP Appliance::COMGETTER(Disks)(ComSafeArrayOut(BSTR, aDisks))
1067{
1068 CheckComArgOutSafeArrayPointerValid(aDisks);
1069
1070 AutoCaller autoCaller(this);
1071 CheckComRCReturnRC(autoCaller.rc());
1072
1073 AutoReadLock alock(this);
1074
1075 size_t c = m->mapDisks.size();
1076 com::SafeArray<BSTR> sfaDisks(c);
1077
1078 DiskImagesMap::const_iterator it;
1079 size_t i = 0;
1080 for (it = m->mapDisks.begin();
1081 it != m->mapDisks.end();
1082 ++it, ++i)
1083 {
1084 // create a string representing this disk
1085 const DiskImage &d = it->second;
1086 char *psz = NULL;
1087 RTStrAPrintf(&psz,
1088 "%s\t"
1089 "%RI64\t"
1090 "%RI64\t"
1091 "%s\t"
1092 "%s\t"
1093 "%RI64\t"
1094 "%RI64\t"
1095 "%s",
1096 d.strDiskId.c_str(),
1097 d.iCapacity,
1098 d.iPopulatedSize,
1099 d.strFormat.c_str(),
1100 d.strHref.c_str(),
1101 d.iSize,
1102 d.iChunkSize,
1103 d.strCompression.c_str());
1104 Utf8Str utf(psz);
1105 Bstr bstr(utf);
1106 // push to safearray
1107 bstr.cloneTo(&sfaDisks[i]);
1108 RTStrFree(psz);
1109 }
1110
1111 sfaDisks.detachTo(ComSafeArrayOutArg(aDisks));
1112
1113 return S_OK;
1114}
1115
1116/**
1117 * Public method implementation.
1118 * @param
1119 * @return
1120 */
1121STDMETHODIMP Appliance::COMGETTER(VirtualSystemDescriptions)(ComSafeArrayOut(IVirtualSystemDescription*, aVirtualSystemDescriptions))
1122{
1123 CheckComArgOutSafeArrayPointerValid(aVirtualSystemDescriptions);
1124
1125 AutoCaller autoCaller(this);
1126 CheckComRCReturnRC(autoCaller.rc());
1127
1128 AutoReadLock alock(this);
1129
1130 SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
1131 sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
1132
1133 return S_OK;
1134}
1135
1136/**
1137 * Public method implementation.
1138 * @param path
1139 * @return
1140 */
1141STDMETHODIMP Appliance::Read(IN_BSTR path)
1142{
1143 HRESULT rc = S_OK;
1144
1145 if (!path)
1146 return E_POINTER;
1147
1148 AutoCaller autoCaller(this);
1149 CheckComRCReturnRC(autoCaller.rc());
1150
1151 AutoWriteLock alock(this);
1152
1153 // see if we can handle this file; for now we insist it has an ".ovf" extension
1154 m->strPath = path;
1155 const char *pcszLastDot = strrchr(m->strPath, '.');
1156 if ( (!pcszLastDot)
1157 || ( strcmp(pcszLastDot, ".ovf")
1158 && strcmp(pcszLastDot, ".OVF")
1159 )
1160 )
1161 return setError(VBOX_E_FILE_ERROR,
1162 tr("Appliance file must have .ovf extension"));
1163
1164 try
1165 {
1166 xml::XmlFileParser parser;
1167 xml::Document doc;
1168 parser.read(m->strPath.raw(),
1169 doc);
1170
1171 const xml::ElementNode *pRootElem = doc.getRootElement();
1172 if (strcmp(pRootElem->getName(), "Envelope"))
1173 return setError(VBOX_E_FILE_ERROR,
1174 tr("Root element in OVF file must be \"Envelope\"."));
1175
1176 // OVF has the following rough layout:
1177 /*
1178 -- <References> .... files referenced from other parts of the file, such as VMDK images
1179 -- Metadata, comprised of several section commands
1180 -- virtual machines, either a single <VirtualSystem>, or a <VirtualSystemCollection>
1181 -- optionally <Strings> for localization
1182 */
1183
1184 // get all "File" child elements of "References" section so we can look up files easily;
1185 // first find the "References" sections so we can look up files
1186 xml::ElementNodesList listFileElements; // receives all /Envelope/References/File nodes
1187 const xml::ElementNode *pReferencesElem;
1188 if ((pReferencesElem = pRootElem->findChildElement("References")))
1189 pReferencesElem->getChildElements(listFileElements, "File");
1190
1191 // now go though the sections
1192 if (!(SUCCEEDED(rc = LoopThruSections(m->strPath.raw(), pReferencesElem, pRootElem))))
1193 return rc;
1194 }
1195 catch(xml::Error &x)
1196 {
1197 return setError(VBOX_E_FILE_ERROR,
1198 x.what());
1199 }
1200
1201 return S_OK;
1202}
1203
1204/**
1205 * Public method implementation.
1206 * @return
1207 */
1208STDMETHODIMP Appliance::Interpret()
1209{
1210 // @todo:
1211 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
1212 // - Appropriate handle errors like not supported file formats
1213 AutoCaller autoCaller(this);
1214 CheckComRCReturnRC(autoCaller.rc());
1215
1216 AutoWriteLock(this);
1217
1218 HRESULT rc = S_OK;
1219
1220 /* Clear any previous virtual system descriptions */
1221 // @todo: have the entries deleted also?
1222 m->virtualSystemDescriptions.clear();
1223
1224 /* We need the default path for storing disk images */
1225 ComPtr<ISystemProperties> systemProps;
1226 rc = mVirtualBox->COMGETTER(SystemProperties)(systemProps.asOutParam());
1227 CheckComRCReturnRC(rc);
1228 Bstr bstrDefaultHardDiskLocation;
1229 rc = systemProps->COMGETTER(DefaultHardDiskFolder)(bstrDefaultHardDiskLocation.asOutParam());
1230 CheckComRCReturnRC(rc);
1231
1232 /* Try/catch so we can clean up on error */
1233 try
1234 {
1235 list<VirtualSystem>::const_iterator it;
1236 /* Iterate through all virtual systems */
1237 for (it = m->llVirtualSystems.begin();
1238 it != m->llVirtualSystems.end();
1239 ++it)
1240 {
1241 const VirtualSystem &vsysThis = *it;
1242
1243 ComObjPtr<VirtualSystemDescription> pNewDesc;
1244 rc = pNewDesc.createObject();
1245 CheckComRCThrowRC(rc);
1246 rc = pNewDesc->init();
1247 CheckComRCThrowRC(rc);
1248
1249 /* Guest OS type */
1250 Utf8Str strOsTypeVBox,
1251 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
1252 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos);
1253 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
1254 "",
1255 strCIMOSType,
1256 strOsTypeVBox);
1257
1258 /* VM name */
1259 /* If the there isn't any name specified create a default one out of
1260 * the OS type */
1261 Utf8Str nameVBox = vsysThis.strName;
1262 if (nameVBox.isEmpty())
1263 nameVBox = strOsTypeVBox;
1264 searchUniqueVMName(nameVBox);
1265 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
1266 "",
1267 vsysThis.strName,
1268 nameVBox);
1269
1270 /* Now that we know the OS type, get our internal defaults based on that. */
1271 ComPtr<IGuestOSType> pGuestOSType;
1272 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
1273 CheckComRCThrowRC(rc);
1274
1275 /* CPU count */
1276 ULONG cpuCountVBox = vsysThis.cCPUs;
1277 /* Check for the constrains */
1278 if (cpuCountVBox > 1) //SchemaDefs::MaxCPUCount)
1279 {
1280 pNewDesc->addWarning(tr("The virtual system claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
1281 cpuCountVBox, 1); //SchemaDefs::MaxCPUCount);
1282 cpuCountVBox = 1; //SchemaDefs::MaxCPUCount;
1283 }
1284 if (vsysThis.cCPUs == 0)
1285 cpuCountVBox = 1;
1286 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
1287 "",
1288 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
1289 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
1290
1291 /* RAM */
1292 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
1293 /* Check for the constrains */
1294 if (ullMemSizeVBox != 0 &&
1295 (ullMemSizeVBox < static_cast<uint64_t>(SchemaDefs::MinGuestRAM) ||
1296 ullMemSizeVBox > static_cast<uint64_t>(SchemaDefs::MaxGuestRAM)))
1297 {
1298 pNewDesc->addWarning(tr("The virtual system claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."),
1299 ullMemSizeVBox, SchemaDefs::MinGuestRAM, SchemaDefs::MaxGuestRAM);
1300 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, static_cast<uint64_t>(SchemaDefs::MinGuestRAM)), static_cast<uint64_t>(SchemaDefs::MaxGuestRAM));
1301 }
1302 if (vsysThis.ullMemorySize == 0)
1303 {
1304 /* If the RAM of the OVF is zero, use our predefined values */
1305 ULONG memSizeVBox2;
1306 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
1307 CheckComRCThrowRC(rc);
1308 /* VBox stores that in MByte */
1309 ullMemSizeVBox = (uint64_t)memSizeVBox2;
1310 }
1311 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
1312 "",
1313 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
1314 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
1315
1316 /* Audio */
1317 if (!vsysThis.strSoundCardType.isNull())
1318 /* Currently we set the AC97 always.
1319 @todo: figure out the hardware which could be possible */
1320 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
1321 "",
1322 vsysThis.strSoundCardType,
1323 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
1324
1325#ifdef VBOX_WITH_USB
1326 /* USB Controller */
1327 if (vsysThis.fHasUsbController)
1328 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
1329#endif /* VBOX_WITH_USB */
1330
1331 /* Network Controller */
1332 // @todo: there is no hardware specification in the OVF file; supposedly the
1333 // hardware will then be determined by the VirtualSystemType element (e.g. "vmx-07")
1334 if (vsysThis.llNetworkNames.size() > 0)
1335 {
1336 /* Check for the constrains */
1337 if (vsysThis.llNetworkNames.size() > SchemaDefs::NetworkAdapterCount)
1338 {
1339 pNewDesc->addWarning(tr("The virtual system claims support for %u network adapters, but VirtualBox has support for max %u network adapter only."),
1340 vsysThis.llNetworkNames.size(), SchemaDefs::NetworkAdapterCount);
1341
1342 }
1343 /* Get the default network adapter type for the selected guest OS */
1344 NetworkAdapterType_T nwAdapterVBox = NetworkAdapterType_Am79C970A;
1345 rc = pGuestOSType->COMGETTER(AdapterType)(&nwAdapterVBox);
1346 CheckComRCThrowRC(rc);
1347 list<Utf8Str>::const_iterator nwIt;
1348 /* Iterate through all abstract networks. We support 8 network
1349 * adapters at the maximum, so the first 8 will be added only. */
1350 size_t a = 0;
1351 for (nwIt = vsysThis.llNetworkNames.begin();
1352 nwIt != vsysThis.llNetworkNames.end() && a < SchemaDefs::NetworkAdapterCount;
1353 ++nwIt, ++a)
1354 {
1355 Utf8Str strNetwork = *nwIt; // logical network to connect to
1356 // make sure it's one of these two
1357 if ( (strNetwork.compareIgnoreCase("Null"))
1358 && (strNetwork.compareIgnoreCase("Bridged"))
1359 && (strNetwork.compareIgnoreCase("Internal"))
1360 && (strNetwork.compareIgnoreCase("HostOnly"))
1361 )
1362 strNetwork = "NAT";
1363
1364 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
1365 "", // ref
1366 strNetwork, // orig
1367 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
1368 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
1369 }
1370 }
1371
1372 /* Floppy Drive */
1373 if (vsysThis.fHasFloppyDrive)
1374 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
1375
1376 /* CD Drive */
1377 /* @todo: I can't disable the CDROM. So nothing to do for now */
1378 /*
1379 if (vsysThis.fHasCdromDrive)
1380 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");*/
1381
1382 /* Hard disk Controller */
1383 uint16_t cIDEused = 0;
1384 uint16_t cSATAused = 0;
1385 uint16_t cSCSIused = 0;
1386 ControllersMap::const_iterator hdcIt;
1387 /* Iterate through all hard disk controllers */
1388 for (hdcIt = vsysThis.mapControllers.begin();
1389 hdcIt != vsysThis.mapControllers.end();
1390 ++hdcIt)
1391 {
1392 const HardDiskController &hdc = hdcIt->second;
1393 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
1394
1395 switch (hdc.system)
1396 {
1397 case HardDiskController::IDE:
1398 {
1399 /* Check for the constrains */
1400 /* @todo: I'm very confused! Are these bits *one* controller or
1401 is every port/bus declared as an extra controller. */
1402 if (cIDEused < 4)
1403 {
1404 // @todo: figure out the IDE types
1405 /* Use PIIX4 as default */
1406 Utf8Str strType = "PIIX4";
1407 if (!RTStrICmp(hdc.strControllerType.c_str(), "PIIX3"))
1408 strType = "PIIX3";
1409 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
1410 strControllerID,
1411 hdc.strControllerType,
1412 strType);
1413 }
1414 else
1415 {
1416 /* Warn only once */
1417 if (cIDEused == 1)
1418 pNewDesc->addWarning(tr("The virtual system claims support for more than one IDE controller, but VirtualBox has support for only one."));
1419
1420 }
1421 ++cIDEused;
1422 break;
1423 }
1424
1425#ifdef VBOX_WITH_AHCI
1426 case HardDiskController::SATA:
1427 {
1428 /* Check for the constrains */
1429 if (cSATAused < 1)
1430 {
1431 // @todo: figure out the SATA types
1432 /* We only support a plain AHCI controller, so use them always */
1433 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
1434 strControllerID,
1435 hdc.strControllerType,
1436 "AHCI");
1437 }
1438 else
1439 {
1440 /* Warn only once */
1441 if (cSATAused == 1)
1442 pNewDesc->addWarning(tr("The virtual system claims support for more than one SATA controller, but VirtualBox has support for only one."));
1443
1444 }
1445 ++cSATAused;
1446 break;
1447 }
1448#endif /* VBOX_WITH_AHCI */
1449
1450 case HardDiskController::SCSI:
1451 {
1452 /* Check for the constrains */
1453 if (cSCSIused < 1)
1454 {
1455 // @todo: figure out the SCSI types
1456 Utf8Str hdcController = "LsiLogic";
1457 /* if (!RTStrICmp(hdc.strControllerType.c_str(), "LsiLogic"))
1458 hdcController = "LsiLogic";
1459 else*/
1460 if (!RTStrICmp(hdc.strControllerType.c_str(), "BusLogic"))
1461 hdcController = "BusLogic";
1462 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
1463 strControllerID,
1464 hdc.strControllerType,
1465 hdcController);
1466 }
1467 else
1468 {
1469 /* Warn only once */
1470 if (cSCSIused == 1)
1471 pNewDesc->addWarning(tr("The virtual system claims support for more than one SCSI controller, but VirtualBox has support for only one."));
1472
1473 }
1474 ++cSCSIused;
1475 break;
1476 }
1477 default:
1478 {
1479 /* @todo: should we stop? */
1480 }
1481 }
1482 }
1483
1484 /* Hard disks */
1485 if (vsysThis.mapVirtualDisks.size() > 0)
1486 {
1487 VirtualDisksMap::const_iterator itVD;
1488 /* Iterate through all hard disks ()*/
1489 for (itVD = vsysThis.mapVirtualDisks.begin();
1490 itVD != vsysThis.mapVirtualDisks.end();
1491 ++itVD)
1492 {
1493 const VirtualDisk &hd = itVD->second;
1494 /* Get the associated disk image */
1495 const DiskImage &di = m->mapDisks[hd.strDiskId];
1496
1497 // @todo:
1498 // - figure out all possible vmdk formats we also support
1499 // - figure out if there is a url specifier for vhd already
1500 // - we need a url specifier for the vdi format
1501 if ( (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#sparse"))
1502 || (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#compressed"))
1503 )
1504 {
1505 /* If the href is empty use the VM name as filename */
1506 Utf8Str strFilename = di.strHref;
1507 if (!strFilename.length())
1508 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
1509 /* Construct a unique target path */
1510 Utf8StrFmt strPath("%ls%c%s",
1511 bstrDefaultHardDiskLocation.raw(),
1512 RTPATH_DELIMITER,
1513 strFilename.c_str());
1514 searchUniqueDiskImageFilePath(strPath);
1515
1516 /* find the description for the hard disk controller
1517 * that has the same ID as hd.idController */
1518 const VirtualSystemDescriptionEntry *pController;
1519 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
1520 throw setError(E_FAIL,
1521 tr("Internal inconsistency looking up hard disk controller."));
1522
1523 /* controller to attach to, and the bus within that controller */
1524 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
1525 pController->ulIndex,
1526 hd.ulAddressOnParent);
1527 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
1528 hd.strDiskId,
1529 di.strHref,
1530 strPath,
1531 strExtraConfig);
1532 }
1533 else
1534 {
1535 /* @todo: should we stop here? */
1536 pNewDesc->addWarning(tr("The virtual system claims support for the following virtual disk image format which VirtualBox not support: %s"),
1537 di.strFormat.c_str());
1538 }
1539 }
1540 }
1541
1542 m->virtualSystemDescriptions.push_back(pNewDesc);
1543 }
1544 }
1545 catch (HRESULT aRC)
1546 {
1547 /* On error we clear the list & return */
1548 m->virtualSystemDescriptions.clear();
1549 rc = aRC;
1550 }
1551
1552 return rc;
1553}
1554
1555/**
1556 * Public method implementation.
1557 * @param aProgress
1558 * @return
1559 */
1560STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
1561{
1562 CheckComArgOutPointerValid(aProgress);
1563
1564 AutoCaller autoCaller(this);
1565 CheckComRCReturnRC(autoCaller.rc());
1566
1567 AutoReadLock(this);
1568
1569 HRESULT rc = S_OK;
1570
1571 ComObjPtr<Progress> progress;
1572 try
1573 {
1574 uint32_t opCount = calcMaxProgress();
1575 Bstr progressDesc = BstrFmt(tr("Import appliance '%s'"),
1576 m->strPath.raw());
1577 /* Create the progress object */
1578 progress.createObject();
1579 rc = progress->init(mVirtualBox, static_cast<IAppliance*>(this),
1580 progressDesc,
1581 FALSE /* aCancelable */,
1582 opCount,
1583 progressDesc);
1584 if (FAILED(rc)) throw rc;
1585
1586 /* Initialize our worker task */
1587 std::auto_ptr<TaskImportMachines> task(new TaskImportMachines(this, progress));
1588 //AssertComRCThrowRC (task->autoCaller.rc());
1589
1590 rc = task->startThread();
1591 if (FAILED(rc)) throw rc;
1592
1593 task.release();
1594 }
1595 catch (HRESULT aRC)
1596 {
1597 rc = aRC;
1598 }
1599
1600 if (SUCCEEDED(rc))
1601 /* Return progress to the caller */
1602 progress.queryInterfaceTo(aProgress);
1603
1604 return rc;
1605}
1606
1607STDMETHODIMP Appliance::Write(IN_BSTR path, IProgress **aProgress)
1608{
1609 HRESULT rc = S_OK;
1610
1611 CheckComArgOutPointerValid(aProgress);
1612
1613 AutoCaller autoCaller(this);
1614 if (FAILED(rc = autoCaller.rc())) return rc;
1615
1616 AutoWriteLock(this);
1617
1618 // see if we can handle this file; for now we insist it has an ".ovf" extension
1619 m->strPath = path;
1620 const char *pcszLastDot = strrchr(m->strPath, '.');
1621 if ( (!pcszLastDot)
1622 || ( strcmp(pcszLastDot, ".ovf")
1623 && strcmp(pcszLastDot, ".OVF")
1624 )
1625 )
1626 return setError(VBOX_E_FILE_ERROR,
1627 tr("Appliance file must have .ovf extension"));
1628
1629 ComObjPtr<Progress> progress;
1630 try
1631 {
1632 uint32_t opCount = calcMaxProgress();
1633 Bstr progressDesc = BstrFmt(tr("Write appliance '%s'"),
1634 m->strPath.raw());
1635 /* Create the progress object */
1636 progress.createObject();
1637 rc = progress->init(mVirtualBox, static_cast<IAppliance*>(this),
1638 progressDesc,
1639 FALSE /* aCancelable */,
1640 opCount,
1641 progressDesc);
1642 CheckComRCThrowRC(rc);
1643
1644 /* Initialize our worker task */
1645 std::auto_ptr<TaskExportOVF> task(new TaskExportOVF(this, progress));
1646 //AssertComRCThrowRC (task->autoCaller.rc());
1647
1648 rc = task->startThread();
1649 CheckComRCThrowRC(rc);
1650
1651 task.release();
1652 }
1653 catch (HRESULT aRC)
1654 {
1655 rc = aRC;
1656 }
1657
1658 if (SUCCEEDED(rc))
1659 /* Return progress to the caller */
1660 progress.queryInterfaceTo(aProgress);
1661
1662 return rc;
1663}
1664
1665HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
1666{
1667 IMachine *machine = NULL;
1668 char *tmpName = RTStrDup(aName.c_str());
1669 int i = 1;
1670 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1671 while (mVirtualBox->FindMachine(Bstr(tmpName), &machine) != VBOX_E_OBJECT_NOT_FOUND)
1672 {
1673 RTStrFree(tmpName);
1674 RTStrAPrintf(&tmpName, "%s_%d", aName.c_str(), i);
1675 ++i;
1676 }
1677 aName = tmpName;
1678 RTStrFree(tmpName);
1679
1680 return S_OK;
1681}
1682
1683HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const
1684{
1685 IHardDisk *harddisk = NULL;
1686 char *tmpName = RTStrDup(aName.c_str());
1687 int i = 1;
1688 /* Check if the file exists or if a file with this path is registered
1689 * already */
1690 /* @todo: Maybe too cost-intensive; try to find a lighter way */
1691 while (RTPathExists(tmpName) ||
1692 mVirtualBox->FindHardDisk(Bstr(tmpName), &harddisk) != VBOX_E_OBJECT_NOT_FOUND)
1693 {
1694 RTStrFree(tmpName);
1695 char *tmpDir = RTStrDup(aName.c_str());
1696 RTPathStripFilename(tmpDir);;
1697 char *tmpFile = RTStrDup(RTPathFilename(aName.c_str()));
1698 RTPathStripExt(tmpFile);
1699 const char *tmpExt = RTPathExt(aName.c_str());
1700 RTStrAPrintf(&tmpName, "%s%c%s_%d%s", tmpDir, RTPATH_DELIMITER, tmpFile, i, tmpExt);
1701 RTStrFree(tmpFile);
1702 RTStrFree(tmpDir);
1703 ++i;
1704 }
1705 aName = tmpName;
1706 RTStrFree(tmpName);
1707
1708 return S_OK;
1709}
1710
1711/**
1712 * Calculates the maximum progress value for importMachines() and write().
1713 * @return
1714 */
1715uint32_t Appliance::calcMaxProgress()
1716{
1717 /* Figure out how many sub operation the import will need */
1718 /* One for the appliance */
1719 uint32_t opCount = 1;
1720 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1721 for (it = m->virtualSystemDescriptions.begin();
1722 it != m->virtualSystemDescriptions.end();
1723 ++it)
1724 {
1725 /* One for every Virtual System */
1726 ++opCount;
1727 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1728 /* One for every hard disk of the Virtual System */
1729 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1730 opCount += (uint32_t)avsdeHDs.size();
1731 }
1732
1733 return opCount;
1734}
1735
1736struct MyHardDiskAttachment
1737{
1738 Guid uuid;
1739 ComPtr<IMachine> pMachine;
1740 StorageBus_T busType;
1741 int32_t lChannel;
1742 int32_t lDevice;
1743};
1744
1745/**
1746 * Worker thread implementation for ImportMachines().
1747 * @param aThread
1748 * @param pvUser
1749 */
1750/* static */
1751DECLCALLBACK(int) Appliance::taskThreadImportMachines(RTTHREAD aThread, void *pvUser)
1752{
1753 std::auto_ptr<TaskImportMachines> task(static_cast<TaskImportMachines*>(pvUser));
1754 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1755
1756 Appliance *pAppliance = task->pAppliance;
1757
1758 LogFlowFuncEnter();
1759 LogFlowFunc(("Appliance %p\n", pAppliance));
1760
1761 AutoCaller autoCaller(pAppliance);
1762 CheckComRCReturnRC(autoCaller.rc());
1763
1764 AutoWriteLock appLock(pAppliance);
1765
1766 HRESULT rc = S_OK;
1767
1768 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
1769
1770 // rollback for errors:
1771 // 1) a list of images that we created/imported
1772 list<MyHardDiskAttachment> llHardDiskAttachments;
1773 list< ComPtr<IHardDisk> > llHardDisksCreated;
1774 list<Guid> llMachinesRegistered;
1775
1776 ComPtr<ISession> session;
1777 bool fSessionOpen = false;
1778 rc = session.createInprocObject(CLSID_Session);
1779 CheckComRCReturnRC(rc);
1780
1781 list<VirtualSystem>::const_iterator it;
1782 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1783 /* Iterate through all virtual systems of that appliance */
1784 size_t i = 0;
1785 for (it = pAppliance->m->llVirtualSystems.begin(),
1786 it1 = pAppliance->m->virtualSystemDescriptions.begin();
1787 it != pAppliance->m->llVirtualSystems.end();
1788 ++it, ++it1, ++i)
1789 {
1790 const VirtualSystem &vsysThis = *it;
1791 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1792
1793 ComPtr<IMachine> pNewMachine;
1794
1795 /* Catch possible errors */
1796 try
1797 {
1798 if (!task->progress.isNull())
1799 task->progress->advanceOperation(BstrFmt(tr("Importing Virtual System %d"), i + 1));
1800
1801 /* How many sub notifications are necessary? */
1802 const float opCountMax = 100.0/5;
1803 uint32_t opCount = 0;
1804
1805 /* Guest OS type */
1806 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1807 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1808 if (vsdeOS.size() < 1)
1809 throw setError(VBOX_E_FILE_ERROR,
1810 tr("Missing guest OS type"));
1811 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1812
1813 /* Now that we know the base system get our internal defaults based on that. */
1814 ComPtr<IGuestOSType> osType;
1815 rc = pVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1816 if (FAILED(rc)) throw rc;
1817
1818 /* Create the machine */
1819 /* First get the name */
1820 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1821 if (vsdeName.size() < 1)
1822 throw setError(VBOX_E_FILE_ERROR,
1823 tr("Missing VM name"));
1824 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1825 rc = pVirtualBox->CreateMachine(Bstr(strNameVBox), Bstr(strOsTypeVBox),
1826 Bstr(), Guid(),
1827 pNewMachine.asOutParam());
1828 if (FAILED(rc)) throw rc;
1829
1830 if (!task->progress.isNull())
1831 rc = task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1832
1833 /* CPU count (ignored for now) */
1834 // EntriesList vsdeCPU = vsd->findByType (VirtualSystemDescriptionType_CPU);
1835
1836 /* RAM */
1837 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1838 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1839 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1840 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1841 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1842 if (FAILED(rc)) throw rc;
1843
1844 /* VRAM */
1845 /* Get the recommended VRAM for this guest OS type */
1846 ULONG vramVBox;
1847 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1848 if (FAILED(rc)) throw rc;
1849
1850 /* Set the VRAM */
1851 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1852 if (FAILED(rc)) throw rc;
1853
1854 if (!task->progress.isNull())
1855 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1856
1857 /* Audio Adapter */
1858 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1859 /* @todo: we support one audio adapter only */
1860 if (vsdeAudioAdapter.size() > 0)
1861 {
1862 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1863 if (RTStrICmp(audioAdapterVBox, "null") != 0)
1864 {
1865 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1866 ComPtr<IAudioAdapter> audioAdapter;
1867 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1868 if (FAILED(rc)) throw rc;
1869 rc = audioAdapter->COMSETTER(Enabled)(true);
1870 if (FAILED(rc)) throw rc;
1871 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1872 if (FAILED(rc)) throw rc;
1873 }
1874 }
1875
1876#ifdef VBOX_WITH_USB
1877 /* USB Controller */
1878 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1879 // USB support is enabled if there's at least one such entry; to disable USB support,
1880 // the type of the USB item would have been changed to "ignore"
1881 bool fUSBEnabled = vsdeUSBController.size() > 0;
1882
1883 ComPtr<IUSBController> usbController;
1884 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1885 if (FAILED(rc)) throw rc;
1886 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1887 if (FAILED(rc)) throw rc;
1888#endif /* VBOX_WITH_USB */
1889
1890 if (!task->progress.isNull())
1891 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1892
1893 /* Change the network adapters */
1894 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1895 if (vsdeNW.size() == 0)
1896 {
1897 /* No network adapters, so we have to disable our default one */
1898 ComPtr<INetworkAdapter> nwVBox;
1899 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1900 if (FAILED(rc)) throw rc;
1901 rc = nwVBox->COMSETTER(Enabled)(false);
1902 if (FAILED(rc)) throw rc;
1903 }
1904 else
1905 {
1906 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1907 /* Iterate through all network cards. We support 8 network adapters
1908 * at the maximum. (@todo: warn if there are more!) */
1909 size_t a = 0;
1910 for (nwIt = vsdeNW.begin();
1911 (nwIt != vsdeNW.end() && a < SchemaDefs::NetworkAdapterCount);
1912 ++nwIt, ++a)
1913 {
1914 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1915
1916 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1917 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1918 ComPtr<INetworkAdapter> pNetworkAdapter;
1919 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1920 if (FAILED(rc)) throw rc;
1921 /* Enable the network card & set the adapter type */
1922 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1923 if (FAILED(rc)) throw rc;
1924 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1925 if (FAILED(rc)) throw rc;
1926
1927 // default is NAT; change to "bridged" if extra conf says so
1928 if (!pvsys->strExtraConfig.compareIgnoreCase("type=Bridged"))
1929 {
1930 rc = pNetworkAdapter->AttachToBridgedInterface();
1931 if (FAILED(rc)) throw rc;
1932 }
1933 }
1934 }
1935
1936 /* Floppy drive */
1937 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1938 // Floppy support is enabled if there's at least one such entry; to disable floppy support,
1939 // the type of the floppy item would have been changed to "ignore"
1940 bool fFloppyEnabled = vsdeFloppy.size() > 0;
1941 ComPtr<IFloppyDrive> floppyDrive;
1942 rc = pNewMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());
1943 if (FAILED(rc)) throw rc;
1944 rc = floppyDrive->COMSETTER(Enabled)(fFloppyEnabled);
1945 if (FAILED(rc)) throw rc;
1946
1947 if (!task->progress.isNull())
1948 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
1949
1950 /* CDROM drive */
1951 /* @todo: I can't disable the CDROM. So nothing to do for now */
1952 // std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsd->findByType(VirtualSystemDescriptionType_CDROM);
1953
1954 /* Hard disk controller IDE */
1955 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1956 /* @todo: we support one IDE controller only */
1957 if (vsdeHDCIDE.size() > 0)
1958 {
1959 /* Set the appropriate IDE controller in the virtual BIOS of the VM */
1960 ComPtr<IBIOSSettings> biosSettings;
1961 rc = pNewMachine->COMGETTER(BIOSSettings)(biosSettings.asOutParam());
1962 if (FAILED(rc)) throw rc;
1963
1964 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
1965 if (!strcmp(pcszIDEType, "PIIX3"))
1966 rc = biosSettings->COMSETTER(IDEControllerType)(IDEControllerType_PIIX3);
1967 else if (!strcmp(pcszIDEType, "PIIX4"))
1968 rc = biosSettings->COMSETTER(IDEControllerType)(IDEControllerType_PIIX4);
1969 else if (!strcmp(pcszIDEType, "ICH6"))
1970 rc = biosSettings->COMSETTER(IDEControllerType)(IDEControllerType_ICH6);
1971 else
1972 throw setError(VBOX_E_FILE_ERROR,
1973 tr("Invalid IDE controller type \"%s\""),
1974 pcszIDEType);
1975 if (FAILED(rc)) throw rc;
1976 }
1977#ifdef VBOX_WITH_AHCI
1978 /* Hard disk controller SATA */
1979 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1980 /* @todo: we support one SATA controller only */
1981 if (vsdeHDCSATA.size() > 0)
1982 {
1983 const Utf8Str &hdcVBox = vsdeHDCIDE.front()->strVbox;
1984 if (hdcVBox == "AHCI")
1985 {
1986 /* For now we have just to enable the AHCI controller. */
1987 ComPtr<ISATAController> hdcSATAVBox;
1988 rc = pNewMachine->COMGETTER(SATAController)(hdcSATAVBox.asOutParam());
1989 if (FAILED(rc)) throw rc;
1990 rc = hdcSATAVBox->COMSETTER(Enabled)(true);
1991 if (FAILED(rc)) throw rc;
1992 }
1993 else
1994 {
1995 throw setError(VBOX_E_FILE_ERROR,
1996 tr("Invalid SATA controller type \"%s\""),
1997 hdcVBox.c_str());
1998 }
1999 }
2000#endif /* VBOX_WITH_AHCI */
2001
2002 /* Hard disk controller SCSI */
2003 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
2004 /* @todo: do we support more than one SCSI controller? */
2005 if (vsdeHDCSCSI.size() > 0)
2006 {
2007 /* @todo: revisit when Main support for SCSI is ready */
2008 }
2009
2010 /* Now its time to register the machine before we add any hard disks */
2011 rc = pVirtualBox->RegisterMachine(pNewMachine);
2012 if (FAILED(rc)) throw rc;
2013
2014 Guid newMachineId;
2015 rc = pNewMachine->COMGETTER(Id)(newMachineId.asOutParam());
2016 if (FAILED(rc)) throw rc;
2017
2018 if (!task->progress.isNull())
2019 task->progress->notifyProgress((uint32_t)(opCountMax * opCount++));
2020
2021 // store new machine for roll-back in case of errors
2022 llMachinesRegistered.push_back(newMachineId);
2023
2024 /* Create the hard disks & connect them to the appropriate controllers. */
2025 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2026 if (avsdeHDs.size() > 0)
2027 {
2028 /* If in the next block an error occur we have to deregister
2029 the machine, so make an extra try/catch block. */
2030 ComPtr<IHardDisk> srcHdVBox;
2031 bool fSourceHdNeedsClosing = false;
2032
2033 try
2034 {
2035 /* In order to attach hard disks we need to open a session
2036 * for the new machine */
2037 rc = pVirtualBox->OpenSession(session, newMachineId);
2038 if (FAILED(rc)) throw rc;
2039 fSessionOpen = true;
2040
2041 /* The disk image has to be on the same place as the OVF file. So
2042 * strip the filename out of the full file path. */
2043 Utf8Str strSrcDir = stripFilename(pAppliance->m->strPath);
2044
2045 /* Iterate over all given disk images */
2046 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
2047 for (itHD = avsdeHDs.begin();
2048 itHD != avsdeHDs.end();
2049 ++itHD)
2050 {
2051 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
2052
2053 const char *pcszDstFilePath = vsdeHD->strVbox.c_str();
2054 /* Check if the destination file exists already or the
2055 * destination path is empty. */
2056 if ( !(*pcszDstFilePath)
2057 || RTPathExists(pcszDstFilePath)
2058 )
2059 /* This isn't allowed */
2060 throw setError(VBOX_E_FILE_ERROR,
2061 tr("Destination file '%s' exists",
2062 pcszDstFilePath));
2063
2064 /* Find the disk from the OVF's disk list */
2065 DiskImagesMap::const_iterator itDiskImage = pAppliance->m->mapDisks.find(vsdeHD->strRef);
2066 /* vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
2067 in the virtual system's disks map under that ID and also in the global images map. */
2068 VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
2069
2070 if ( itDiskImage == pAppliance->m->mapDisks.end()
2071 || itVirtualDisk == vsysThis.mapVirtualDisks.end()
2072 )
2073 throw setError(E_FAIL,
2074 tr("Internal inconsistency looking up disk images."));
2075
2076 const DiskImage &di = itDiskImage->second;
2077 const VirtualDisk &vd = itVirtualDisk->second;
2078
2079 /* Make sure all target directories exists */
2080 rc = VirtualBox::ensureFilePathExists(pcszDstFilePath);
2081 if (FAILED(rc))
2082 throw rc;
2083
2084 ComPtr<IProgress> progress;
2085
2086 ComPtr<IHardDisk> dstHdVBox;
2087 /* If strHref is empty we have to create a new file */
2088 if (di.strHref.isNull() ||
2089 di.strHref.c_str()[0] == 0)
2090 {
2091 /* Which format to use? */
2092 Bstr srcFormat = L"VDI";
2093 if ( (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#sparse"))
2094 || (!RTStrICmp(di.strFormat.c_str(), "http://www.vmware.com/specifications/vmdk.html#compressed")))
2095 srcFormat = L"VMDK";
2096 /* Create an empty hard disk */
2097 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2098 if (FAILED(rc)) throw rc;
2099
2100 /* Create a dynamic growing disk image with the given capacity */
2101 rc = dstHdVBox->CreateDynamicStorage(di.iCapacity / _1M, progress.asOutParam());
2102 if (FAILED(rc)) throw rc;
2103
2104 /* Advance to the next operation */
2105 if (!task->progress.isNull())
2106 task->progress->advanceOperation (BstrFmt(tr("Creating virtual disk image '%s'"), pcszDstFilePath));
2107 }
2108 else
2109 {
2110 /* Construct the source file path */
2111 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
2112 /* Check if the source file exists */
2113 if (!RTPathExists(strSrcFilePath.c_str()))
2114 /* This isn't allowed */
2115 throw setError(VBOX_E_FILE_ERROR,
2116 tr("Source virtual disk image file '%s' doesn't exist"),
2117 strSrcFilePath.c_str());
2118
2119 /* Clone the disk image (this is necessary cause the id has
2120 * to be recreated for the case the same hard disk is
2121 * attached already from a previous import) */
2122
2123 /* First open the existing disk image */
2124 rc = pVirtualBox->OpenHardDisk(Bstr(strSrcFilePath), srcHdVBox.asOutParam());
2125 if (FAILED(rc)) throw rc;
2126 fSourceHdNeedsClosing = true;
2127
2128 /* We need the format description of the source disk image */
2129 Bstr srcFormat;
2130 rc = srcHdVBox->COMGETTER(Format)(srcFormat.asOutParam());
2131 if (FAILED(rc)) throw rc;
2132 /* Create a new hard disk interface for the destination disk image */
2133 rc = pVirtualBox->CreateHardDisk(srcFormat, Bstr(pcszDstFilePath), dstHdVBox.asOutParam());
2134 if (FAILED(rc)) throw rc;
2135 /* Clone the source disk image */
2136 rc = srcHdVBox->CloneTo(dstHdVBox, progress.asOutParam());
2137 if (FAILED(rc)) throw rc;
2138
2139 /* Advance to the next operation */
2140 if (!task->progress.isNull())
2141 task->progress->advanceOperation (BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()));
2142 }
2143
2144 // now loop until the asynchronous operation completes and then
2145 // report its result
2146 BOOL fCompleted;
2147 LONG currentPercent;
2148 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
2149 {
2150 rc = progress->COMGETTER(Percent(&currentPercent));
2151 if (FAILED(rc)) throw rc;
2152 if (!task->progress.isNull())
2153 task->progress->notifyProgress(currentPercent);
2154 if (fCompleted)
2155 break;
2156 /* Make sure the loop is not too tight */
2157 rc = progress->WaitForCompletion(100);
2158 if (FAILED(rc)) throw rc;
2159 }
2160 // report result of asynchronous operation
2161 HRESULT vrc;
2162 rc = progress->COMGETTER(ResultCode)(&vrc);
2163 if (FAILED(rc)) throw rc;
2164
2165 // if the thread of the progress object has an error, then
2166 // retrieve the error info from there, or it'll be lost
2167 if (FAILED(vrc))
2168 {
2169 com::ErrorInfo info(progress);
2170 const char *pcsz = Utf8Str(info.getText()).c_str();
2171 HRESULT rc2 = setError(vrc,
2172 pcsz);
2173 throw rc2;
2174 }
2175
2176 if (fSourceHdNeedsClosing)
2177 {
2178 rc = srcHdVBox->Close();
2179 if (FAILED(rc)) throw rc;
2180 fSourceHdNeedsClosing = false;
2181 }
2182
2183 llHardDisksCreated.push_back(dstHdVBox);
2184
2185 /* Now use the new uuid to attach the disk image to our new machine */
2186 ComPtr<IMachine> sMachine;
2187 rc = session->COMGETTER(Machine)(sMachine.asOutParam());
2188 if (FAILED(rc)) throw rc;
2189 Guid hdId;
2190 rc = dstHdVBox->COMGETTER(Id)(hdId.asOutParam());
2191 if (FAILED(rc)) throw rc;
2192
2193 /* For now we assume we have one controller of every type only */
2194 HardDiskController hdc = (*vsysThis.mapControllers.find(vd.idController)).second;
2195
2196 // this is for rollback later
2197 MyHardDiskAttachment mhda;
2198 mhda.uuid = newMachineId;
2199 mhda.pMachine = pNewMachine;
2200 mhda.busType = StorageBus_IDE;
2201
2202 switch (hdc.system)
2203 {
2204 case HardDiskController::IDE:
2205 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
2206 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
2207 // the device number can be either 0 or 1, to specify the master or the slave device,
2208 // respectively. For the secondary IDE controller, the device number is always 1 because
2209 // the master device is reserved for the CD-ROM drive.
2210 switch (vd.ulAddressOnParent)
2211 {
2212 case 0: // interpret this as primary master
2213 mhda.lChannel = (long)0;
2214 mhda.lDevice = (long)0;
2215 break;
2216
2217 case 1: // interpret this as primary slave
2218 mhda.lChannel = (long)0;
2219 mhda.lDevice = (long)1;
2220 break;
2221
2222 case 2: // interpret this as secondary slave
2223 mhda.lChannel = (long)1;
2224 mhda.lDevice = (long)1;
2225 break;
2226
2227 default:
2228 throw setError(VBOX_E_NOT_SUPPORTED,
2229 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), vd.ulAddressOnParent);
2230 break;
2231 }
2232 break;
2233
2234 case HardDiskController::SATA:
2235 mhda.busType = StorageBus_SATA;
2236 mhda.lChannel = (long)vd.ulAddressOnParent;
2237 mhda.lDevice = (long)0;
2238 break;
2239
2240 case HardDiskController::SCSI:
2241// mhda.busType = StorageBus_SCSI;
2242 throw setError(VBOX_E_NOT_SUPPORTED,
2243 tr("SCSI controller support is not available yet in VirtualBox"));
2244 // @todo
2245 break;
2246
2247 default: break;
2248 }
2249
2250 Log(("Attaching disk %s to channel %d on device %d\n", pcszDstFilePath, mhda.lChannel, mhda.lDevice));
2251
2252 rc = sMachine->AttachHardDisk(hdId,
2253 mhda.busType,
2254 mhda.lChannel,
2255 mhda.lDevice);
2256 if (FAILED(rc)) throw rc;
2257
2258 llHardDiskAttachments.push_back(mhda);
2259
2260 rc = sMachine->SaveSettings();
2261 if (FAILED(rc)) throw rc;
2262 } // end for (itHD = avsdeHDs.begin();
2263
2264 // only now that we're done with all disks, close the session
2265 rc = session->Close();
2266 if (FAILED(rc)) throw rc;
2267 fSessionOpen = false;
2268 }
2269 catch(HRESULT /* aRC */)
2270 {
2271 if (fSourceHdNeedsClosing)
2272 srcHdVBox->Close();
2273
2274 if (fSessionOpen)
2275 session->Close();
2276
2277 throw;
2278 }
2279 }
2280 }
2281 catch(HRESULT aRC)
2282 {
2283 rc = aRC;
2284 }
2285
2286 if (FAILED(rc))
2287 break;
2288
2289 } // for (it = pAppliance->m->llVirtualSystems.begin(),
2290
2291 if (FAILED(rc))
2292 {
2293 // with _whatever_ error we've had, do a complete roll-back of
2294 // machines and disks we've created; unfortunately this is
2295 // not so trivially done...
2296
2297 HRESULT rc2;
2298 // detach all hard disks from all machines we created
2299 list<MyHardDiskAttachment>::iterator itM;
2300 for (itM = llHardDiskAttachments.begin();
2301 itM != llHardDiskAttachments.end();
2302 ++itM)
2303 {
2304 const MyHardDiskAttachment &mhda = *itM;
2305 rc2 = pVirtualBox->OpenSession(session, mhda.uuid);
2306 if (SUCCEEDED(rc2))
2307 {
2308 ComPtr<IMachine> sMachine;
2309 rc2 = session->COMGETTER(Machine)(sMachine.asOutParam());
2310 if (SUCCEEDED(rc2))
2311 {
2312 rc2 = sMachine->DetachHardDisk(mhda.busType, mhda.lChannel, mhda.lDevice);
2313 rc2 = sMachine->SaveSettings();
2314 }
2315 session->Close();
2316 }
2317 }
2318
2319 // now clean up all hard disks we created
2320 list< ComPtr<IHardDisk> >::iterator itHD;
2321 for (itHD = llHardDisksCreated.begin();
2322 itHD != llHardDisksCreated.end();
2323 ++itHD)
2324 {
2325 ComPtr<IHardDisk> pDisk = *itHD;
2326 ComPtr<IProgress> pProgress;
2327 rc2 = pDisk->DeleteStorage(pProgress.asOutParam());
2328 rc2 = pProgress->WaitForCompletion(-1);
2329 }
2330
2331 // finally, deregister and remove all machines
2332 list<Guid>::iterator itID;
2333 for (itID = llMachinesRegistered.begin();
2334 itID != llMachinesRegistered.end();
2335 ++itID)
2336 {
2337 const Guid &guid = *itID;
2338 ComPtr<IMachine> failedMachine;
2339 rc2 = pVirtualBox->UnregisterMachine(guid, failedMachine.asOutParam());
2340 if (SUCCEEDED(rc2))
2341 rc2 = failedMachine->DeleteSettings();
2342 }
2343 }
2344
2345 task->rc = rc;
2346
2347 if (!task->progress.isNull())
2348 task->progress->notifyComplete(rc);
2349
2350 LogFlowFunc(("rc=%Rhrc\n", rc));
2351 LogFlowFuncLeave();
2352
2353 return VINF_SUCCESS;
2354}
2355
2356/**
2357 * Worker thread implementation for ImportMachines().
2358 * @param aThread
2359 * @param pvUser
2360 */
2361/* static */
2362DECLCALLBACK(int) Appliance::taskThreadExportOVF(RTTHREAD aThread, void *pvUser)
2363{
2364 std::auto_ptr<TaskImportMachines> task(static_cast<TaskImportMachines*>(pvUser));
2365 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
2366
2367 Appliance *pAppliance = task->pAppliance;
2368
2369 LogFlowFuncEnter();
2370 LogFlowFunc(("Appliance %p\n", pAppliance));
2371
2372 AutoCaller autoCaller(pAppliance);
2373 CheckComRCReturnRC(autoCaller.rc());
2374
2375 AutoWriteLock appLock(pAppliance);
2376
2377 HRESULT rc = S_OK;
2378
2379 ComPtr<IVirtualBox> pVirtualBox(pAppliance->mVirtualBox);
2380
2381 try
2382 {
2383 xml::Document doc;
2384 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
2385
2386 pelmRoot->setAttribute("ovf:version", "1.0");
2387 pelmRoot->setAttribute("xml:lang", "en-US");
2388 pelmRoot->setAttribute("xmlns", "http://schemas.dmtf.org/ovf/envelope/1");
2389 pelmRoot->setAttribute("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1");
2390 pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
2391 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
2392 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
2393 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
2394 pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
2395
2396
2397 // <Envelope>/<References>
2398 xml::ElementNode *pelmReferences = pelmRoot->createChild("References");
2399 // @ŧodo
2400
2401 /* <Envelope>/<DiskSection>:
2402 <DiskSection>
2403 <Info>List of the virtual disks used in the package</Info>
2404 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="http://www.vmware.com/specifications/vmdk.html#compressed" ovf:populatedSize="1924967692"/>
2405 </DiskSection> */
2406 xml::ElementNode *pelmDiskSection = pelmRoot->createChild("DiskSection");
2407 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
2408 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
2409 // @todo for each disk:
2410 // xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
2411
2412 /* <Envelope>/<NetworkSection>:
2413 <NetworkSection>
2414 <Info>Logical networks used in the package</Info>
2415 <Network ovf:name="VM Network">
2416 <Description>The network that the LAMP Service will be available on</Description>
2417 </Network>
2418 </NetworkSection> */
2419 xml::ElementNode *pelmNetworkSection = pelmRoot->createChild("NetworkSection");
2420 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
2421 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
2422 // for now, set up a map so we have a list of unique network names (to make
2423 // sure the same network name is only added once)
2424 map<Utf8Str, bool> mapNetworks;
2425 // we fill this later below when we iterate over the networks
2426
2427 // and here come the virtual systems:
2428 xml::ElementNode *pelmVirtualSystemCollection = pelmRoot->createChild("VirtualSystemCollection");
2429 xml::AttributeNode *pattrVirtualSystemCollectionId = pelmVirtualSystemCollection->setAttribute("ovf:id", "ExportedVirtualBoxMachines"); // whatever
2430
2431 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2432 /* Iterate through all virtual systems of that appliance */
2433 for (it = pAppliance->m->virtualSystemDescriptions.begin();
2434 it != pAppliance->m->virtualSystemDescriptions.end();
2435 ++it)
2436 {
2437 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2438
2439 xml::ElementNode *pelmVirtualSystem = pelmVirtualSystemCollection->createChild("VirtualSystem");
2440 xml::ElementNode *pelmVirtualSystemInfo = pelmVirtualSystem->createChild("Info"); // @todo put in description here after implementing an entry for it
2441
2442 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2443
2444 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
2445 if (llOS.size() != 1)
2446 throw setError(VBOX_E_NOT_SUPPORTED,
2447 tr("Missing OS type"));
2448 /* <OperatingSystemSection ovf:id="82">
2449 <Info>Guest Operating System</Info>
2450 <Description>Linux 2.6.x</Description>
2451 </OperatingSystemSection> */
2452 xml::ElementNode *pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
2453 pelmOperatingSystemSection->setAttribute("ovf:id", llOS.front()->strOvf);
2454 // @todo convert vbox OS type into OVF ID
2455 pelmOperatingSystemSection->createChild("Info")->addContent("blah"); // @ŧodo
2456 pelmOperatingSystemSection->createChild("Description")->addContent("blah"); // @ŧodo
2457
2458 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
2459 xml::ElementNode *pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
2460
2461 /* <System>
2462 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
2463 <vssd:ElementName>vmware</vssd:ElementName>
2464 <vssd:InstanceID>1</vssd:InstanceID>
2465 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
2466 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2467 </System> */
2468 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
2469
2470 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
2471 xml::ElementNode *pelmVirtualSystemType = pelmSystem->createChild("VirtualSystemType");
2472 pelmVirtualSystemType->addContent("virtualbox-2.2"); // instead of vmx-7?
2473
2474 uint32_t ulInstanceID = 1;
2475
2476 list<VirtualSystemDescriptionEntry>::const_iterator itD;
2477 for (itD = vsdescThis->m->llDescriptions.begin();
2478 itD != vsdescThis->m->llDescriptions.end();
2479 ++itD)
2480 {
2481 const VirtualSystemDescriptionEntry &desc = *itD;
2482
2483 OVFResourceType_T type = (OVFResourceType_T)0; // if this becomes != 0 then we do stuff
2484 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
2485 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
2486
2487 int32_t lVirtualQuantity = -1;
2488 Utf8Str strAllocationUnits;
2489
2490 bool fAutomaticAllocation = false;
2491 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
2492
2493 uint64_t uTemp;
2494
2495 switch (desc.type)
2496 {
2497 case VirtualSystemDescriptionType_CPU:
2498 /* <Item>
2499 <rasd:Caption>1 virtual CPU</rasd:Caption>
2500 <rasd:Description>Number of virtual CPUs</rasd:Description>
2501 <rasd:ElementName>virtual CPU</rasd:ElementName>
2502 <rasd:InstanceID>1</rasd:InstanceID>
2503 <rasd:ResourceType>3</rasd:ResourceType>
2504 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2505 </Item> */
2506 strDescription = "Number of virtual CPUs";
2507 type = OVFResourceType_Processor; // 3
2508 lVirtualQuantity = 1;
2509 break;
2510
2511 case VirtualSystemDescriptionType_Memory:
2512 /* <Item>
2513 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
2514 <rasd:Caption>256 MB of memory</rasd:Caption>
2515 <rasd:Description>Memory Size</rasd:Description>
2516 <rasd:ElementName>Memory</rasd:ElementName>
2517 <rasd:InstanceID>2</rasd:InstanceID>
2518 <rasd:ResourceType>4</rasd:ResourceType>
2519 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
2520 </Item> */
2521 strDescription = "Memory Size";
2522 type = OVFResourceType_Memory; // 4
2523 desc.strVbox.toInt(uTemp);
2524 lVirtualQuantity = (int32_t)(uTemp / _1M);
2525 strAllocationUnits = "MegaBytes";
2526 break;
2527
2528// case VirtualSystemDescriptionType_HardDiskControllerIDE:
2529// strDescription = "Memory Size";
2530// type = OVFResourceType_Memory; // 4
2531// desc.strVbox.toInt(uTemp);
2532// lVirtualQuantity = (int32_t)(uTemp / _1M);
2533// break;
2534
2535// case VirtualSystemDescriptionType_HardDiskControllerSATA:
2536// strDescription = "Memory Size";
2537// type = OVFResourceType_Memory; // 4
2538// desc.strVbox.toInt(uTemp);
2539// lVirtualQuantity = (int32_t)(uTemp / _1M);
2540// break;
2541
2542// case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2543// strDescription = "Memory Size";
2544// type = OVFResourceType_Memory; // 4
2545// desc.strVbox.toInt(uTemp);
2546// lVirtualQuantity = (int32_t)(uTemp / _1M);
2547// break;
2548
2549// case VirtualSystemDescriptionType_HardDiskImage:
2550// strDescription = "Memory Size";
2551// type = OVFResourceType_Memory; // 4
2552// desc.strVbox.toInt(uTemp);
2553// lVirtualQuantity = (int32_t)(uTemp / _1M);
2554// break;
2555
2556// case VirtualSystemDescriptionType_Floppy:
2557// strDescription = "Memory Size";
2558// type = OVFResourceType_Memory; // 4
2559// desc.strVbox.toInt(uTemp);
2560// lVirtualQuantity = (int32_t)(uTemp / _1M);
2561// break;
2562
2563// case VirtualSystemDescriptionType_CDROM:
2564// strDescription = "Memory Size";
2565// type = OVFResourceType_Memory; // 4
2566// desc.strVbox.toInt(uTemp);
2567// lVirtualQuantity = (int32_t)(uTemp / _1M);
2568// break;
2569
2570 case VirtualSystemDescriptionType_NetworkAdapter:
2571 /* <Item>
2572 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
2573 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
2574 <rasd:Connection>VM Network</rasd:Connection>
2575 <rasd:ElementName>VM network</rasd:ElementName>
2576 <rasd:InstanceID>3</rasd:InstanceID>
2577 <rasd:ResourceType>10</rasd:ResourceType>
2578 </Item> */
2579 fAutomaticAllocation = true;
2580 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
2581 type = OVFResourceType_EthernetAdapter; // 10
2582 strConnection = desc.strOvf;
2583
2584 mapNetworks[desc.strOvf] = true;
2585 break;
2586
2587// case VirtualSystemDescriptionType_USBController:
2588// strDescription = "Memory Size";
2589// type = OVFResourceType_Memory; // 4
2590// desc.strVbox.toInt(uTemp);
2591// lVirtualQuantity = (int32_t)(uTemp / _1M);
2592// break;
2593
2594/* case VirtualSystemDescriptionType_SoundCard:
2595 strDescription = "Memory Size";
2596 type = OVFResourceType_Memory; // 4
2597 desc.strVbox.toInt(uTemp);
2598 lVirtualQuantity = (int32_t)(uTemp / _1M);
2599 break;*/
2600 }
2601
2602 if (type)
2603 {
2604 xml::ElementNode *pItem;
2605 pItem = pelmVirtualHardwareSection->createChild("Item");
2606
2607 if (!strAllocationUnits.isEmpty())
2608 pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
2609
2610 if (fAutomaticAllocation)
2611 pItem->createChild("rasd:AutomaticAllocation")->addContent("true");
2612
2613 if (!strDescription.isEmpty())
2614 pItem->createChild("rasd:Description")->addContent(strDescription);
2615 if (!strCaption.isEmpty())
2616 pItem->createChild("rasd:Caption")->addContent(strCaption);
2617
2618 if (!strConnection.isEmpty())
2619 pItem->createChild("rasd:Connection")->addContent(strConnection);
2620
2621 // <rasd:InstanceID>1</rasd:InstanceID>
2622 pItem->createChild("rasd:InstanceID")->addContent(Utf8StrFmt("%d", ulInstanceID));
2623 ++ulInstanceID;
2624
2625 // <rasd:ResourceType>3</rasd:ResourceType>
2626 pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
2627
2628 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2629 if (lVirtualQuantity != -1)
2630 pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
2631
2632 }
2633 }
2634 }
2635
2636 // finally, fill in the network section we set up empty above according
2637 // to the networks we found with the hardware items
2638 map<Utf8Str, bool>::const_iterator itN;
2639 for (itN = mapNetworks.begin();
2640 itN != mapNetworks.end();
2641 ++itN)
2642 {
2643 const Utf8Str &strNetwork = itN->first;
2644 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
2645 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
2646 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
2647 }
2648
2649 // now go write the XML
2650 xml::XmlFileWriter writer(doc);
2651 writer.write(pAppliance->m->strPath.c_str());
2652 }
2653 catch(xml::Error &x)
2654 {
2655 rc = setError(VBOX_E_FILE_ERROR,
2656 x.what());
2657 }
2658 catch(HRESULT aRC)
2659 {
2660 rc = aRC;
2661 }
2662
2663 task->rc = rc;
2664
2665 if (!task->progress.isNull())
2666 task->progress->notifyComplete(rc);
2667
2668 LogFlowFunc(("rc=%Rhrc\n", rc));
2669 LogFlowFuncLeave();
2670
2671 return VINF_SUCCESS;
2672}
2673
2674////////////////////////////////////////////////////////////////////////////////
2675//
2676// IVirtualSystemDescription constructor / destructor
2677//
2678////////////////////////////////////////////////////////////////////////////////
2679
2680DEFINE_EMPTY_CTOR_DTOR(VirtualSystemDescription)
2681struct shutup3 {};
2682
2683/**
2684 * COM initializer.
2685 * @return
2686 */
2687HRESULT VirtualSystemDescription::init()
2688{
2689 /* Enclose the state transition NotReady->InInit->Ready */
2690 AutoInitSpan autoInitSpan(this);
2691 AssertReturn(autoInitSpan.isOk(), E_FAIL);
2692
2693 /* Initialize data */
2694 m = new Data();
2695
2696 /* Confirm a successful initialization */
2697 autoInitSpan.setSucceeded();
2698 return S_OK;
2699}
2700
2701/**
2702* COM uninitializer.
2703*/
2704
2705void VirtualSystemDescription::uninit()
2706{
2707 delete m;
2708 m = NULL;
2709}
2710
2711////////////////////////////////////////////////////////////////////////////////
2712//
2713// IVirtualSystemDescription public methods
2714//
2715////////////////////////////////////////////////////////////////////////////////
2716
2717/**
2718 * Public method implementation.
2719 * @param
2720 * @return
2721 */
2722STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount)
2723{
2724 if (!aCount)
2725 return E_POINTER;
2726
2727 AutoCaller autoCaller(this);
2728 CheckComRCReturnRC(autoCaller.rc());
2729
2730 AutoReadLock alock(this);
2731
2732 *aCount = (ULONG)m->llDescriptions.size();
2733
2734 return S_OK;
2735}
2736
2737/**
2738 * Public method implementation.
2739 * @return
2740 */
2741STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes),
2742 ComSafeArrayOut(BSTR, aRefs),
2743 ComSafeArrayOut(BSTR, aOrigValues),
2744 ComSafeArrayOut(BSTR, aVboxValues),
2745 ComSafeArrayOut(BSTR, aExtraConfigValues))
2746{
2747 if (ComSafeArrayOutIsNull(aTypes) ||
2748 ComSafeArrayOutIsNull(aRefs) ||
2749 ComSafeArrayOutIsNull(aOrigValues) ||
2750 ComSafeArrayOutIsNull(aVboxValues) ||
2751 ComSafeArrayOutIsNull(aExtraConfigValues))
2752 return E_POINTER;
2753
2754 AutoCaller autoCaller(this);
2755 CheckComRCReturnRC(autoCaller.rc());
2756
2757 AutoReadLock alock(this);
2758
2759 ULONG c = (ULONG)m->llDescriptions.size();
2760 com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c);
2761 com::SafeArray<BSTR> sfaRefs(c);
2762 com::SafeArray<BSTR> sfaOrigValues(c);
2763 com::SafeArray<BSTR> sfaVboxValues(c);
2764 com::SafeArray<BSTR> sfaExtraConfigValues(c);
2765
2766 list<VirtualSystemDescriptionEntry>::const_iterator it;
2767 size_t i = 0;
2768 for (it = m->llDescriptions.begin();
2769 it != m->llDescriptions.end();
2770 ++it, ++i)
2771 {
2772 const VirtualSystemDescriptionEntry &vsde = (*it);
2773
2774 sfaTypes[i] = vsde.type;
2775
2776 Bstr bstr = vsde.strRef;
2777 bstr.cloneTo(&sfaRefs[i]);
2778
2779 bstr = vsde.strOvf;
2780 bstr.cloneTo(&sfaOrigValues[i]);
2781
2782 bstr = vsde.strVbox;
2783 bstr.cloneTo(&sfaVboxValues[i]);
2784
2785 bstr = vsde.strExtraConfig;
2786 bstr.cloneTo(&sfaExtraConfigValues[i]);
2787 }
2788
2789 sfaTypes.detachTo(ComSafeArrayOutArg(aTypes));
2790 sfaRefs.detachTo(ComSafeArrayOutArg(aRefs));
2791 sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues));
2792 sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues));
2793 sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues));
2794
2795 return S_OK;
2796}
2797
2798/**
2799 * Public method implementation.
2800 * @return
2801 */
2802STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled),
2803 ComSafeArrayIn(IN_BSTR, argVboxValues),
2804 ComSafeArrayIn(IN_BSTR, argExtraConfigValues))
2805{
2806 CheckComArgSafeArrayNotNull(argVboxValues);
2807 CheckComArgSafeArrayNotNull(argExtraConfigValues);
2808
2809 AutoCaller autoCaller(this);
2810 CheckComRCReturnRC(autoCaller.rc());
2811
2812 AutoWriteLock alock(this);
2813
2814 com::SafeArray<IN_BSTR> aVboxValues(ComSafeArrayInArg(argVboxValues));
2815 com::SafeArray<IN_BSTR> aExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues));
2816
2817 if ( (aVboxValues.size() != m->llDescriptions.size())
2818 || (aExtraConfigValues.size() != m->llDescriptions.size())
2819 )
2820 return E_INVALIDARG;
2821
2822 list<VirtualSystemDescriptionEntry>::iterator it;
2823 size_t i = 0;
2824 for (it = m->llDescriptions.begin();
2825 it != m->llDescriptions.end();
2826 ++it, ++i)
2827 {
2828 VirtualSystemDescriptionEntry& vsde = *it;
2829
2830 if (aEnabled[i])
2831 {
2832 vsde.strVbox = aVboxValues[i];
2833 vsde.strExtraConfig = aExtraConfigValues[i];
2834 }
2835 else
2836 vsde.type = VirtualSystemDescriptionType_Ignore;
2837 }
2838
2839 return S_OK;
2840}
2841
2842/**
2843* Public method implementation.
2844 * @return
2845 */
2846STDMETHODIMP VirtualSystemDescription::GetWarnings(ComSafeArrayOut(BSTR, aWarnings))
2847{
2848 if (ComSafeArrayOutIsNull(aWarnings))
2849 return E_POINTER;
2850
2851 AutoCaller autoCaller(this);
2852 CheckComRCReturnRC(autoCaller.rc());
2853
2854 AutoReadLock alock(this);
2855
2856 com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
2857
2858 list<Utf8Str>::const_iterator it;
2859 size_t i = 0;
2860 for (it = m->llWarnings.begin();
2861 it != m->llWarnings.end();
2862 ++it, ++i)
2863 {
2864 Bstr bstr = *it;
2865 bstr.cloneTo(&sfaWarnings[i]);
2866 }
2867
2868 sfaWarnings.detachTo(ComSafeArrayOutArg(aWarnings));
2869
2870 return S_OK;
2871}
2872
2873/**
2874 * Internal method; adds a new description item to the member list.
2875 * @param aType Type of description for the new item.
2876 * @param strRef Reference item; only used with hard disk controllers.
2877 * @param aOrigValue Corresponding original value from OVF.
2878 * @param aAutoValue Initial configuration value (can be overridden by caller with setFinalValues).
2879 * @param strExtraConfig Extra configuration; meaning dependent on type.
2880 */
2881void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType,
2882 const Utf8Str &strRef,
2883 const Utf8Str &aOrigValue,
2884 const Utf8Str &aAutoValue,
2885 const Utf8Str &strExtraConfig /*= ""*/)
2886{
2887 VirtualSystemDescriptionEntry vsde;
2888 vsde.ulIndex = (uint32_t)m->llDescriptions.size(); // each entry gets an index so the client side can reference them
2889 vsde.type = aType;
2890 vsde.strRef = strRef;
2891 vsde.strOvf = aOrigValue;
2892 vsde.strVbox = aAutoValue;
2893 vsde.strExtraConfig = strExtraConfig;
2894
2895 m->llDescriptions.push_back(vsde);
2896}
2897
2898void VirtualSystemDescription::addWarning(const char* aWarning, ...)
2899{
2900 va_list args;
2901 va_start(args, aWarning);
2902 Utf8StrFmtVA str(aWarning, args);
2903 va_end(args);
2904 m->llWarnings.push_back(str);
2905}
2906
2907/**
2908 * Private method; returns a list of description items containing all the items from the member
2909 * description items of this virtual system that match the given type.
2910 * @param aType
2911 * @return
2912 */
2913std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(VirtualSystemDescriptionType_T aType)
2914{
2915 std::list<VirtualSystemDescriptionEntry*> vsd;
2916
2917 list<VirtualSystemDescriptionEntry>::iterator it;
2918 for (it = m->llDescriptions.begin();
2919 it != m->llDescriptions.end();
2920 ++it)
2921 {
2922 if (it->type == aType)
2923 vsd.push_back(&(*it));
2924 }
2925
2926 return vsd;
2927}
2928
2929/**
2930 * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with
2931 * the given reference ID. Useful when needing the controller for a particular
2932 * virtual disk.
2933 * @param id
2934 * @return
2935 */
2936const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFromID(uint32_t id)
2937{
2938 Utf8Str strRef = Utf8StrFmt("%RI32", id);
2939 list<VirtualSystemDescriptionEntry>::const_iterator it;
2940 for (it = m->llDescriptions.begin();
2941 it != m->llDescriptions.end();
2942 ++it)
2943 {
2944 switch (it->type)
2945 {
2946 case VirtualSystemDescriptionType_HardDiskControllerIDE:
2947 case VirtualSystemDescriptionType_HardDiskControllerSATA:
2948 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
2949 if (it->strRef == strRef)
2950 return &(*it);
2951 break;
2952 }
2953 }
2954
2955 return NULL;
2956}
2957
2958////////////////////////////////////////////////////////////////////////////////
2959//
2960// IMachine public methods
2961//
2962////////////////////////////////////////////////////////////////////////////////
2963
2964// This code is here so we won't have to include the appliance headers in the
2965// IMachine implementation, and we also need to access private appliance data.
2966
2967/**
2968* Public method implementation.
2969* @param appliance
2970* @return
2971*/
2972
2973STDMETHODIMP Machine::Export(IAppliance *appliance)
2974{
2975 HRESULT rc = S_OK;
2976
2977 if (!appliance)
2978 return E_POINTER;
2979
2980 AutoCaller autoCaller(this);
2981 CheckComRCReturnRC(autoCaller.rc());
2982
2983 AutoReadLock alock(this);
2984
2985 ComObjPtr<VirtualSystemDescription> pNewDesc;
2986
2987 try
2988 {
2989 Bstr bstrName;
2990 Bstr bstrDescription;
2991 Bstr bstrGuestOSType;
2992 uint32_t cCPUs;
2993 uint32_t ulMemSizeMB;
2994 BOOL fDVDEnabled;
2995 BOOL fFloppyEnabled;
2996 ComPtr<IUSBController> pUsbController;
2997 ComPtr<IAudioAdapter> pAudioAdapter;
2998
2999 // get name
3000 bstrName = mUserData->mName;
3001 // get description
3002 bstrName = mUserData->mDescription;
3003 // get guest OS
3004 bstrGuestOSType = mUserData->mOSTypeId;
3005 // CPU count
3006 cCPUs = mHWData->mCPUCount;
3007 // memory size in MB
3008 ulMemSizeMB = mHWData->mMemorySize;
3009 // VRAM size?
3010 // BIOS settings?
3011 // 3D acceleration enabled?
3012 // hardware virtualization enabled?
3013 // nested paging enabled?
3014 // HWVirtExVPIDEnabled?
3015 // PAEEnabled?
3016 // snapshotFolder?
3017 // VRDPServer?
3018
3019 // floppy
3020 rc = mFloppyDrive->COMGETTER(Enabled)(&fFloppyEnabled);
3021 if (FAILED(rc)) throw rc;
3022
3023 // CD-ROM ?!?
3024 // ComPtr<IDVDDrive> pDVDDrive;
3025 fDVDEnabled = 1;
3026
3027 // this is more tricky so use the COM method
3028 rc = COMGETTER(USBController)(pUsbController.asOutParam());
3029 if (FAILED(rc)) throw rc;
3030
3031 pAudioAdapter = mAudioAdapter;
3032
3033 // create a new virtual system
3034 rc = pNewDesc.createObject();
3035 CheckComRCThrowRC(rc);
3036 rc = pNewDesc->init();
3037 CheckComRCThrowRC(rc);
3038
3039 /* Guest OS type */
3040 Utf8Str strOsTypeVBox(bstrGuestOSType);
3041 CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str());
3042 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
3043 "",
3044 Utf8StrFmt("%RI32", cim),
3045 strOsTypeVBox);
3046
3047 /* VM name */
3048 Utf8Str strVMName(bstrName);
3049 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
3050 "",
3051 strVMName,
3052 Utf8Str(bstrName));
3053
3054 /* CPU count*/
3055 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
3056 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
3057 "",
3058 strCpuCount,
3059 strCpuCount);
3060
3061 /* Memory */
3062 Utf8Str strMemory = Utf8StrFmt("%RI32", (uint64_t)ulMemSizeMB * _1M);
3063 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
3064 "",
3065 strMemory,
3066 strMemory);
3067
3068 uint32_t uControllerId = 1;
3069 uint32_t uidIdeController;
3070 uint32_t uidSataController;
3071
3072// <const name="HardDiskControllerIDE" value="6" />
3073 ComPtr<IBIOSSettings> pBiosSettings;
3074 pBiosSettings = mBIOSSettings;
3075 Utf8Str strVbox;
3076 IDEControllerType_T ctlr;
3077 rc = pBiosSettings->COMGETTER(IDEControllerType)(&ctlr);
3078 if (FAILED(rc)) throw rc;
3079 switch(ctlr)
3080 {
3081 case IDEControllerType_PIIX3: strVbox = "PIIX3"; break;
3082 case IDEControllerType_PIIX4: strVbox = "PIIX4"; break;
3083 case IDEControllerType_ICH6: strVbox = "ICH6"; break;
3084 }
3085
3086 if (strVbox.length())
3087 {
3088 uidIdeController = uControllerId++;
3089 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE, Utf8StrFmt("%d", uidIdeController), strVbox, "");
3090 }
3091
3092#ifdef VBOX_WITH_AHCI
3093// <const name="HardDiskControllerSATA" value="7" />
3094 ComPtr<ISATAController> pSataController;
3095 pSataController = mSATAController;
3096 BOOL fSataEnabled;
3097 rc = pSataController->COMGETTER(Enabled)(&fSataEnabled);
3098 if (FAILED(rc)) throw rc;
3099 if (fSataEnabled)
3100 {
3101 uidSataController = uControllerId++;
3102 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA, Utf8StrFmt("%d", uidSataController), strVbox, "");
3103 }
3104#endif // VBOX_WITH_AHCI
3105
3106// <const name="HardDiskControllerSCSI" value="8" />
3107 // @todo
3108
3109// <const name="HardDiskImage" value="9" />
3110 // hardDiskAttachments
3111// mHDData->mAttachments @todo
3112 HDData::AttachmentList::iterator itA;
3113 for (itA = mHDData->mAttachments.begin();
3114 itA != mHDData->mAttachments.end();
3115 ++itA)
3116 {
3117 ComObjPtr<HardDiskAttachment> pHDA = *itA;
3118
3119 // the attachment's data
3120 ComPtr<IHardDisk> pHardDisk;
3121 StorageBus_T storageBus;
3122 LONG lChannel;
3123 LONG lDevice;
3124
3125 // and how this translates to the virtual system
3126 LONG lChannelVsys;
3127
3128 rc = pHDA->COMGETTER(HardDisk)(pHardDisk.asOutParam());
3129 if (FAILED(rc)) throw rc;
3130
3131 rc = pHDA->COMGETTER(Bus)(&storageBus);
3132 if (FAILED(rc)) throw rc;
3133
3134 rc = pHDA->COMGETTER(Channel)(&lChannel);
3135 if (FAILED(rc)) throw rc;
3136
3137 rc = pHDA->COMGETTER(Device)(&lDevice);
3138 if (FAILED(rc)) throw rc;
3139
3140 Bstr bstrLocation;
3141 rc = pHardDisk->COMGETTER(Location)(bstrLocation.asOutParam());
3142 Bstr bstrName;
3143 rc = pHardDisk->COMGETTER(Name)(bstrName.asOutParam());
3144
3145 uint32_t uidControllerVsys;
3146
3147 switch (storageBus)
3148 {
3149 case HardDiskController::IDE:
3150 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
3151 // and it must be updated when that is changed!
3152
3153 if (lChannel == 0 && lDevice == 0) // primary master
3154 lChannelVsys = 0;
3155 else if (lChannel == 0 && lDevice == 1) // primary slave
3156 lChannelVsys = 1;
3157 else if (lChannel == 1 && lDevice == 1) // secondary slave; secondary master is always CDROM
3158 lChannelVsys = 2;
3159 else
3160 throw setError(VBOX_E_NOT_SUPPORTED,
3161 tr("Cannot handle hard disk attachment: channel is %d, device is %d"), lChannel, lDevice);
3162 break;
3163
3164 uidControllerVsys = uidIdeController;
3165 break;
3166
3167 case HardDiskController::SATA:
3168 lChannelVsys = lChannel; // should be between 0 and 29
3169 uidControllerVsys = uidSataController;
3170 break;
3171
3172 case HardDiskController::SCSI:
3173 // mhda.busType = StorageBus_SCSI;
3174 throw setError(VBOX_E_NOT_SUPPORTED,
3175 tr("SCSI controller support is not available yet in VirtualBox"));
3176 // @todo
3177 break;
3178
3179 default: break;
3180 }
3181
3182 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
3183 Utf8Str(bstrName), // disk ID: let's use the name
3184 "", // OVF value: unknown as of now
3185 Utf8Str(bstrLocation), // vbox value: media path
3186 Utf8StrFmt("controller=%d;channel=%d", uidControllerVsys, lChannelVsys));
3187 }
3188
3189 /* Floppy Drive */
3190 if (fFloppyEnabled)
3191 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
3192
3193 /* CD Drive */
3194 if (fDVDEnabled)
3195 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
3196
3197// <const name="LogicalNetwork" value="12" />
3198
3199// <const name="NetworkAdapter" value="13" />
3200 size_t a;
3201 for (a = 0;
3202 a < SchemaDefs::NetworkAdapterCount;
3203 ++a)
3204 {
3205 ComPtr<INetworkAdapter> pNetworkAdapter;
3206 BOOL fEnabled;
3207 NetworkAdapterType_T adapterType;
3208 NetworkAttachmentType_T attachmentType;
3209
3210 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
3211 if (FAILED(rc)) throw rc;
3212 /* Enable the network card & set the adapter type */
3213 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
3214 if (FAILED(rc)) throw rc;
3215
3216 if (fEnabled)
3217 {
3218 Utf8Str strAttachmentType;
3219
3220 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
3221 if (FAILED(rc)) throw rc;
3222
3223 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
3224 if (FAILED(rc)) throw rc;
3225
3226 switch (attachmentType)
3227 {
3228 case NetworkAttachmentType_Null:
3229 strAttachmentType = "Null";
3230 break;
3231
3232 case NetworkAttachmentType_NAT:
3233 strAttachmentType = "NAT";
3234 break;
3235
3236 case NetworkAttachmentType_Bridged:
3237 strAttachmentType = "Bridged";
3238 break;
3239
3240 case NetworkAttachmentType_Internal:
3241 strAttachmentType = "Internal";
3242 break;
3243
3244 case NetworkAttachmentType_HostOnly:
3245 strAttachmentType = "HostOnly";
3246 break;
3247 }
3248
3249 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
3250 "", // ref
3251 strAttachmentType, // orig
3252 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
3253 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
3254 }
3255 }
3256
3257// <const name="USBController" value="14" />
3258
3259// <const name="SoundCard" value="15" />
3260
3261 // finally, add the virtual system to the appliance
3262 Appliance *pAppliance = static_cast<Appliance*>(appliance);
3263 AutoCaller autoCaller(pAppliance);
3264 if (FAILED(rc)) throw rc;
3265
3266 AutoWriteLock alock(pAppliance);
3267
3268 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
3269 }
3270 catch(HRESULT arc)
3271 {
3272 rc = arc;
3273 }
3274
3275 return rc;
3276}
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