VirtualBox

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

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

OVF: wrong error code returned

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