VirtualBox

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

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

OVF: try to fix windows burn

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