VirtualBox

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

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

OVF: not necessary anymore

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