VirtualBox

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

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

OVF: implement multiple channels per HD controller

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette