VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/ApplianceImplExport.cpp@ 74897

Last change on this file since 74897 was 74886, checked in by vboxsync, 6 years ago

Main/Appliance: mark the OCI specific code for moving to the extpack (it can't work to have specific code in the generic API, the rules will be always too volatile), and additionally fix lock order issues originating in the "too smart" code for figuring out the first disk. The previous code was getting the base medium which was kind of a workaround for limitations in the image upload logic, which were temporary and are about to be resolved.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 128.8 KB
Line 
1/* $Id: ApplianceImplExport.cpp 74886 2018-10-17 15:21:01Z vboxsync $ */
2/** @file
3 * IAppliance and IVirtualSystem COM class implementations.
4 */
5
6/*
7 * Copyright (C) 2008-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/path.h>
19#include <iprt/dir.h>
20#include <iprt/param.h>
21#include <iprt/s3.h>
22#include <iprt/manifest.h>
23#include <iprt/stream.h>
24#include <iprt/zip.h>
25
26#include <VBox/version.h>
27
28#include "ApplianceImpl.h"
29#include "VirtualBoxImpl.h"
30#include "ProgressImpl.h"
31#include "MachineImpl.h"
32#include "MediumImpl.h"
33#include "Global.h"
34#include "MediumFormatImpl.h"
35#include "SystemPropertiesImpl.h"
36
37#include "AutoCaller.h"
38#include "Logging.h"
39
40#include "ApplianceImplPrivate.h"
41
42//#include "OCIProvider.h"
43//#include "CloudClientImpl.h"
44//#include "OCIProfile.h"
45//#include "CloudAPI.h"
46//#include "VBoxOCIApi.h"
47//#include "VBoxOCIRest.h"
48
49using namespace std;
50
51////////////////////////////////////////////////////////////////////////////////
52//
53// IMachine public methods
54//
55////////////////////////////////////////////////////////////////////////////////
56
57// This code is here so we won't have to include the appliance headers in the
58// IMachine implementation, and we also need to access private appliance data.
59
60/**
61* Public method implementation.
62* @param aAppliance Appliance object.
63* @param aLocation Where to store the appliance.
64* @param aDescription Appliance description.
65* @return
66*/
67HRESULT Machine::exportTo(const ComPtr<IAppliance> &aAppliance, const com::Utf8Str &aLocation,
68 ComPtr<IVirtualSystemDescription> &aDescription)
69{
70 HRESULT rc = S_OK;
71
72 if (!aAppliance)
73 return E_POINTER;
74
75 ComObjPtr<VirtualSystemDescription> pNewDesc;
76
77 try
78 {
79 IAppliance *iAppliance = aAppliance;
80 Appliance *pAppliance = static_cast<Appliance*>(iAppliance);
81
82 LocationInfo locInfo;
83 i_parseURI(aLocation, locInfo);
84
85 Utf8Str strBasename(locInfo.strPath);
86 strBasename.stripPath().stripSuffix();
87 if (locInfo.strPath.endsWith(".tar.gz", Utf8Str::CaseSensitive))
88 strBasename.stripSuffix();
89
90 // create a new virtual system to store in the appliance
91 rc = pNewDesc.createObject();
92 if (FAILED(rc)) throw rc;
93 rc = pNewDesc->init();
94 if (FAILED(rc)) throw rc;
95
96 // store the machine object so we can dump the XML in Appliance::Write()
97 pNewDesc->m->pMachine = this;
98
99 // first, call the COM methods, as they request locks
100 BOOL fUSBEnabled = FALSE;
101 com::SafeIfaceArray<IUSBController> usbControllers;
102 rc = COMGETTER(USBControllers)(ComSafeArrayAsOutParam(usbControllers));
103 if (SUCCEEDED(rc))
104 {
105 for (unsigned i = 0; i < usbControllers.size(); ++i)
106 {
107 USBControllerType_T enmType;
108
109 rc = usbControllers[i]->COMGETTER(Type)(&enmType);
110 if (FAILED(rc)) throw rc;
111
112 if (enmType == USBControllerType_OHCI)
113 fUSBEnabled = TRUE;
114 }
115 }
116
117 // request the machine lock while accessing internal members
118 AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS);
119
120 ComPtr<IAudioAdapter> pAudioAdapter = mAudioAdapter;
121 BOOL fAudioEnabled;
122 rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
123 if (FAILED(rc)) throw rc;
124 AudioControllerType_T audioController;
125 rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
126 if (FAILED(rc)) throw rc;
127
128 // get name
129 Utf8Str strVMName = mUserData->s.strName;
130 // get description
131 Utf8Str strDescription = mUserData->s.strDescription;
132 // get guest OS
133 Utf8Str strOsTypeVBox = mUserData->s.strOsType;
134 // CPU count
135 uint32_t cCPUs = mHWData->mCPUCount;
136 // memory size in MB
137 uint32_t ulMemSizeMB = mHWData->mMemorySize;
138 // VRAM size?
139 // BIOS settings?
140 // 3D acceleration enabled?
141 // hardware virtualization enabled?
142 // nested paging enabled?
143 // HWVirtExVPIDEnabled?
144 // PAEEnabled?
145 // Long mode enabled?
146 BOOL fLongMode;
147 rc = GetCPUProperty(CPUPropertyType_LongMode, &fLongMode);
148 if (FAILED(rc)) throw rc;
149
150 // snapshotFolder?
151 // VRDPServer?
152
153 /* Guest OS type */
154 ovf::CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str(), fLongMode);
155 pNewDesc->i_addEntry(VirtualSystemDescriptionType_OS,
156 "",
157 Utf8StrFmt("%RI32", cim),
158 strOsTypeVBox);
159
160 /* VM name */
161 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Name,
162 "",
163 strVMName,
164 strVMName);
165
166 // description
167 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Description,
168 "",
169 strDescription,
170 strDescription);
171
172 /* CPU count*/
173 Utf8Str strCpuCount = Utf8StrFmt("%RI32", cCPUs);
174 pNewDesc->i_addEntry(VirtualSystemDescriptionType_CPU,
175 "",
176 strCpuCount,
177 strCpuCount);
178
179 /* Memory */
180 Utf8Str strMemory = Utf8StrFmt("%RI64", (uint64_t)ulMemSizeMB * _1M);
181 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Memory,
182 "",
183 strMemory,
184 strMemory);
185
186 // the one VirtualBox IDE controller has two channels with two ports each, which is
187 // considered two IDE controllers with two ports each by OVF, so export it as two
188 int32_t lIDEControllerPrimaryIndex = 0;
189 int32_t lIDEControllerSecondaryIndex = 0;
190 int32_t lSATAControllerIndex = 0;
191 int32_t lSCSIControllerIndex = 0;
192
193 /* Fetch all available storage controllers */
194 com::SafeIfaceArray<IStorageController> nwControllers;
195 rc = COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(nwControllers));
196 if (FAILED(rc)) throw rc;
197
198 ComPtr<IStorageController> pIDEController;
199 ComPtr<IStorageController> pSATAController;
200 ComPtr<IStorageController> pSCSIController;
201 ComPtr<IStorageController> pSASController;
202 for (size_t j = 0; j < nwControllers.size(); ++j)
203 {
204 StorageBus_T eType;
205 rc = nwControllers[j]->COMGETTER(Bus)(&eType);
206 if (FAILED(rc)) throw rc;
207 if ( eType == StorageBus_IDE
208 && pIDEController.isNull())
209 pIDEController = nwControllers[j];
210 else if ( eType == StorageBus_SATA
211 && pSATAController.isNull())
212 pSATAController = nwControllers[j];
213 else if ( eType == StorageBus_SCSI
214 && pSATAController.isNull())
215 pSCSIController = nwControllers[j];
216 else if ( eType == StorageBus_SAS
217 && pSASController.isNull())
218 pSASController = nwControllers[j];
219 }
220
221// <const name="HardDiskControllerIDE" value="6" />
222 if (!pIDEController.isNull())
223 {
224 StorageControllerType_T ctlr;
225 rc = pIDEController->COMGETTER(ControllerType)(&ctlr);
226 if (FAILED(rc)) throw rc;
227
228 Utf8Str strVBox;
229 switch (ctlr)
230 {
231 case StorageControllerType_PIIX3: strVBox = "PIIX3"; break;
232 case StorageControllerType_PIIX4: strVBox = "PIIX4"; break;
233 case StorageControllerType_ICH6: strVBox = "ICH6"; break;
234 default: break; /* Shut up MSC. */
235 }
236
237 if (strVBox.length())
238 {
239 lIDEControllerPrimaryIndex = (int32_t)pNewDesc->m->maDescriptions.size();
240 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
241 Utf8StrFmt("%d", lIDEControllerPrimaryIndex), // strRef
242 strVBox, // aOvfValue
243 strVBox); // aVBoxValue
244 lIDEControllerSecondaryIndex = lIDEControllerPrimaryIndex + 1;
245 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
246 Utf8StrFmt("%d", lIDEControllerSecondaryIndex),
247 strVBox,
248 strVBox);
249 }
250 }
251
252// <const name="HardDiskControllerSATA" value="7" />
253 if (!pSATAController.isNull())
254 {
255 Utf8Str strVBox = "AHCI";
256 lSATAControllerIndex = (int32_t)pNewDesc->m->maDescriptions.size();
257 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
258 Utf8StrFmt("%d", lSATAControllerIndex),
259 strVBox,
260 strVBox);
261 }
262
263// <const name="HardDiskControllerSCSI" value="8" />
264 if (!pSCSIController.isNull())
265 {
266 StorageControllerType_T ctlr;
267 rc = pSCSIController->COMGETTER(ControllerType)(&ctlr);
268 if (SUCCEEDED(rc))
269 {
270 Utf8Str strVBox = "LsiLogic"; // the default in VBox
271 switch (ctlr)
272 {
273 case StorageControllerType_LsiLogic: strVBox = "LsiLogic"; break;
274 case StorageControllerType_BusLogic: strVBox = "BusLogic"; break;
275 default: break; /* Shut up MSC. */
276 }
277 lSCSIControllerIndex = (int32_t)pNewDesc->m->maDescriptions.size();
278 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
279 Utf8StrFmt("%d", lSCSIControllerIndex),
280 strVBox,
281 strVBox);
282 }
283 else
284 throw rc;
285 }
286
287 if (!pSASController.isNull())
288 {
289 // VirtualBox considers the SAS controller a class of its own but in OVF
290 // it should be a SCSI controller
291 Utf8Str strVBox = "LsiLogicSas";
292 lSCSIControllerIndex = (int32_t)pNewDesc->m->maDescriptions.size();
293 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskControllerSAS,
294 Utf8StrFmt("%d", lSCSIControllerIndex),
295 strVBox,
296 strVBox);
297 }
298
299// <const name="HardDiskImage" value="9" />
300// <const name="Floppy" value="18" />
301// <const name="CDROM" value="19" />
302
303 for (MediumAttachmentList::const_iterator
304 it = mMediumAttachments->begin();
305 it != mMediumAttachments->end();
306 ++it)
307 {
308 ComObjPtr<MediumAttachment> pHDA = *it;
309
310 // the attachment's data
311 ComPtr<IMedium> pMedium;
312 ComPtr<IStorageController> ctl;
313 Bstr controllerName;
314
315 rc = pHDA->COMGETTER(Controller)(controllerName.asOutParam());
316 if (FAILED(rc)) throw rc;
317
318 rc = GetStorageControllerByName(controllerName.raw(), ctl.asOutParam());
319 if (FAILED(rc)) throw rc;
320
321 StorageBus_T storageBus;
322 DeviceType_T deviceType;
323 LONG lChannel;
324 LONG lDevice;
325
326 rc = ctl->COMGETTER(Bus)(&storageBus);
327 if (FAILED(rc)) throw rc;
328
329 rc = pHDA->COMGETTER(Type)(&deviceType);
330 if (FAILED(rc)) throw rc;
331
332 rc = pHDA->COMGETTER(Medium)(pMedium.asOutParam());
333 if (FAILED(rc)) throw rc;
334
335 rc = pHDA->COMGETTER(Port)(&lChannel);
336 if (FAILED(rc)) throw rc;
337
338 rc = pHDA->COMGETTER(Device)(&lDevice);
339 if (FAILED(rc)) throw rc;
340
341 Utf8Str strTargetImageName;
342 Utf8Str strLocation;
343 LONG64 llSize = 0;
344
345 if ( deviceType == DeviceType_HardDisk
346 && pMedium)
347 {
348 Bstr bstrLocation;
349
350 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
351 if (FAILED(rc)) throw rc;
352 strLocation = bstrLocation;
353
354 // find the source's base medium for two things:
355 // 1) we'll use its name to determine the name of the target disk, which is readable,
356 // as opposed to the UUID filename of a differencing image, if pMedium is one
357 // 2) we need the size of the base image so we can give it to addEntry(), and later
358 // on export, the progress will be based on that (and not the diff image)
359 ComPtr<IMedium> pBaseMedium;
360 rc = pMedium->COMGETTER(Base)(pBaseMedium.asOutParam());
361 // returns pMedium if there are no diff images
362 if (FAILED(rc)) throw rc;
363
364 strTargetImageName = Utf8StrFmt("%s-disk%.3d.vmdk", strBasename.c_str(), ++pAppliance->m->cDisks);
365 if (strTargetImageName.length() > RTTAR_NAME_MAX)
366 throw setError(VBOX_E_NOT_SUPPORTED,
367 tr("Cannot attach disk '%s' -- file name too long"), strTargetImageName.c_str());
368
369 // force reading state, or else size will be returned as 0
370 MediumState_T ms;
371 rc = pBaseMedium->RefreshState(&ms);
372 if (FAILED(rc)) throw rc;
373
374 rc = pBaseMedium->COMGETTER(Size)(&llSize);
375 if (FAILED(rc)) throw rc;
376
377 /* If the medium is encrypted add the key identifier to the list. */
378 IMedium *iBaseMedium = pBaseMedium;
379 Medium *pBase = static_cast<Medium*>(iBaseMedium);
380 const com::Utf8Str strKeyId = pBase->i_getKeyId();
381 if (!strKeyId.isEmpty())
382 {
383 IMedium *iMedium = pMedium;
384 Medium *pMed = static_cast<Medium*>(iMedium);
385 com::Guid mediumUuid = pMed->i_getId();
386 bool fKnown = false;
387
388 /* Check whether the ID is already in our sequence, add it otherwise. */
389 for (unsigned i = 0; i < pAppliance->m->m_vecPasswordIdentifiers.size(); i++)
390 {
391 if (strKeyId.equals(pAppliance->m->m_vecPasswordIdentifiers[i]))
392 {
393 fKnown = true;
394 break;
395 }
396 }
397
398 if (!fKnown)
399 {
400 GUIDVEC vecMediumIds;
401
402 vecMediumIds.push_back(mediumUuid);
403 pAppliance->m->m_vecPasswordIdentifiers.push_back(strKeyId);
404 pAppliance->m->m_mapPwIdToMediumIds.insert(std::pair<com::Utf8Str, GUIDVEC>(strKeyId, vecMediumIds));
405 }
406 else
407 {
408 std::map<com::Utf8Str, GUIDVEC>::iterator itMap = pAppliance->m->m_mapPwIdToMediumIds.find(strKeyId);
409 if (itMap == pAppliance->m->m_mapPwIdToMediumIds.end())
410 throw setError(E_FAIL, tr("Internal error adding a medium UUID to the map"));
411 itMap->second.push_back(mediumUuid);
412 }
413 }
414 }
415 else if ( deviceType == DeviceType_DVD
416 && pMedium)
417 {
418 /*
419 * check the minimal rules to grant access to export an image
420 * 1. no host drive CD/DVD image
421 * 2. the image must be accessible and readable
422 * 3. only ISO image is exported
423 */
424
425 //1. no host drive CD/DVD image
426 BOOL fHostDrive = false;
427 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
428 if (FAILED(rc)) throw rc;
429
430 if(fHostDrive)
431 continue;
432
433 //2. the image must be accessible and readable
434 MediumState_T ms;
435 rc = pMedium->RefreshState(&ms);
436 if (FAILED(rc)) throw rc;
437
438 if (ms != MediumState_Created)
439 continue;
440
441 //3. only ISO image is exported
442 Bstr bstrLocation;
443 rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam());
444 if (FAILED(rc)) throw rc;
445
446 strLocation = bstrLocation;
447
448 Utf8Str ext = strLocation;
449 ext.assignEx(RTPathSuffix(ext.c_str()));//returns extension with dot (".iso")
450
451 int eq = ext.compare(".iso", Utf8Str::CaseInsensitive);
452 if (eq != 0)
453 continue;
454
455 strTargetImageName = Utf8StrFmt("%s-disk%.3d.iso", strBasename.c_str(), ++pAppliance->m->cDisks);
456 if (strTargetImageName.length() > RTTAR_NAME_MAX)
457 throw setError(VBOX_E_NOT_SUPPORTED,
458 tr("Cannot attach image '%s' -- file name too long"), strTargetImageName.c_str());
459
460 rc = pMedium->COMGETTER(Size)(&llSize);
461 if (FAILED(rc)) throw rc;
462 }
463 // and how this translates to the virtual system
464 int32_t lControllerVsys = 0;
465 LONG lChannelVsys;
466
467 switch (storageBus)
468 {
469 case StorageBus_IDE:
470 // this is the exact reverse to what we're doing in Appliance::taskThreadImportMachines,
471 // and it must be updated when that is changed!
472 // Before 3.2 we exported one IDE controller with channel 0-3, but we now maintain
473 // compatibility with what VMware does and export two IDE controllers with two channels each
474
475 if (lChannel == 0 && lDevice == 0) // primary master
476 {
477 lControllerVsys = lIDEControllerPrimaryIndex;
478 lChannelVsys = 0;
479 }
480 else if (lChannel == 0 && lDevice == 1) // primary slave
481 {
482 lControllerVsys = lIDEControllerPrimaryIndex;
483 lChannelVsys = 1;
484 }
485 else if (lChannel == 1 && lDevice == 0) // secondary master; by default this is the CD-ROM but
486 // as of VirtualBox 3.1 that can change
487 {
488 lControllerVsys = lIDEControllerSecondaryIndex;
489 lChannelVsys = 0;
490 }
491 else if (lChannel == 1 && lDevice == 1) // secondary slave
492 {
493 lControllerVsys = lIDEControllerSecondaryIndex;
494 lChannelVsys = 1;
495 }
496 else
497 throw setError(VBOX_E_NOT_SUPPORTED,
498 tr("Cannot handle medium attachment: channel is %d, device is %d"), lChannel, lDevice);
499 break;
500
501 case StorageBus_SATA:
502 lChannelVsys = lChannel; // should be between 0 and 29
503 lControllerVsys = lSATAControllerIndex;
504 break;
505
506 case StorageBus_SCSI:
507 case StorageBus_SAS:
508 lChannelVsys = lChannel; // should be between 0 and 15
509 lControllerVsys = lSCSIControllerIndex;
510 break;
511
512 case StorageBus_Floppy:
513 lChannelVsys = 0;
514 lControllerVsys = 0;
515 break;
516
517 default:
518 throw setError(VBOX_E_NOT_SUPPORTED,
519 tr("Cannot handle medium attachment: storageBus is %d, channel is %d, device is %d"),
520 storageBus, lChannel, lDevice);
521 }
522
523 Utf8StrFmt strExtra("controller=%RI32;channel=%RI32", lControllerVsys, lChannelVsys);
524 Utf8Str strEmpty;
525
526 switch (deviceType)
527 {
528 case DeviceType_HardDisk:
529 Log(("Adding VirtualSystemDescriptionType_HardDiskImage, disk size: %RI64\n", llSize));
530 pNewDesc->i_addEntry(VirtualSystemDescriptionType_HardDiskImage,
531 strTargetImageName, // disk ID: let's use the name
532 strTargetImageName, // OVF value:
533 strLocation, // vbox value: media path
534 (uint32_t)(llSize / _1M),
535 strExtra);
536 break;
537
538 case DeviceType_DVD:
539 Log(("Adding VirtualSystemDescriptionType_CDROM, disk size: %RI64\n", llSize));
540 pNewDesc->i_addEntry(VirtualSystemDescriptionType_CDROM,
541 strTargetImageName, // disk ID
542 strTargetImageName, // OVF value
543 strLocation, // vbox value
544 (uint32_t)(llSize / _1M),// ulSize
545 strExtra);
546 break;
547
548 case DeviceType_Floppy:
549 pNewDesc->i_addEntry(VirtualSystemDescriptionType_Floppy,
550 strEmpty, // disk ID
551 strEmpty, // OVF value
552 strEmpty, // vbox value
553 1, // ulSize
554 strExtra);
555 break;
556
557 default: break; /* Shut up MSC. */
558 }
559 }
560
561// <const name="NetworkAdapter" />
562 uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(i_getChipsetType());
563 size_t a;
564 for (a = 0; a < maxNetworkAdapters; ++a)
565 {
566 ComPtr<INetworkAdapter> pNetworkAdapter;
567 BOOL fEnabled;
568 NetworkAdapterType_T adapterType;
569 NetworkAttachmentType_T attachmentType;
570
571 rc = GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
572 if (FAILED(rc)) throw rc;
573 /* Enable the network card & set the adapter type */
574 rc = pNetworkAdapter->COMGETTER(Enabled)(&fEnabled);
575 if (FAILED(rc)) throw rc;
576
577 if (fEnabled)
578 {
579 rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType);
580 if (FAILED(rc)) throw rc;
581
582 rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType);
583 if (FAILED(rc)) throw rc;
584
585 Utf8Str strAttachmentType = convertNetworkAttachmentTypeToString(attachmentType);
586 pNewDesc->i_addEntry(VirtualSystemDescriptionType_NetworkAdapter,
587 "", // ref
588 strAttachmentType, // orig
589 Utf8StrFmt("%RI32", (uint32_t)adapterType), // conf
590 0,
591 Utf8StrFmt("type=%s", strAttachmentType.c_str())); // extra conf
592 }
593 }
594
595// <const name="USBController" />
596#ifdef VBOX_WITH_USB
597 if (fUSBEnabled)
598 pNewDesc->i_addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
599#endif /* VBOX_WITH_USB */
600
601// <const name="SoundCard" />
602 if (fAudioEnabled)
603 pNewDesc->i_addEntry(VirtualSystemDescriptionType_SoundCard,
604 "",
605 "ensoniq1371", // this is what OVFTool writes and VMware supports
606 Utf8StrFmt("%RI32", audioController));
607
608 /* We return the new description to the caller */
609 ComPtr<IVirtualSystemDescription> copy(pNewDesc);
610 copy.queryInterfaceTo(aDescription.asOutParam());
611
612 AutoWriteLock alock(pAppliance COMMA_LOCKVAL_SRC_POS);
613 // finally, add the virtual system to the appliance
614 pAppliance->m->virtualSystemDescriptions.push_back(pNewDesc);
615 }
616 catch(HRESULT arc)
617 {
618 rc = arc;
619 }
620
621 return rc;
622}
623
624////////////////////////////////////////////////////////////////////////////////
625//
626// IAppliance public methods
627//
628////////////////////////////////////////////////////////////////////////////////
629
630/**
631 * Public method implementation.
632 * @param aFormat Appliance format.
633 * @param aOptions Export options.
634 * @param aPath Path to write the appliance to.
635 * @param aProgress Progress object.
636 * @return
637 */
638HRESULT Appliance::write(const com::Utf8Str &aFormat,
639 const std::vector<ExportOptions_T> &aOptions,
640 const com::Utf8Str &aPath,
641 ComPtr<IProgress> &aProgress)
642{
643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
644
645 m->optListExport.clear();
646 if (aOptions.size())
647 {
648 for (size_t i = 0; i < aOptions.size(); ++i)
649 {
650 m->optListExport.insert(i, aOptions[i]);
651 }
652 }
653
654 HRESULT rc = S_OK;
655// AssertReturn(!(m->optListExport.contains(ExportOptions_CreateManifest)
656// && m->optListExport.contains(ExportOptions_ExportDVDImages)), E_INVALIDARG);
657
658 /* Parse all necessary info out of the URI */
659 i_parseURI(aPath, m->locInfo);
660
661 /** @todo r=klaus all of this code should be made fully generic, applicable
662 * to any cloud provider. This implies changing the method names accordingly
663 * and (more importantly) moving all the cloud specific checking code to the
664 * actual ICloudClient implementation in the corresponding extpack. It will
665 * move the check to the async task, but that's a bearable cost, not getting
666 * the error straight from the API call but a tiny bit later through the
667 * Progress object. */
668 if (m->locInfo.storageType == VFSType_OCI)//(isCloudDestination(aPath))
669 {
670 rc = S_OK;
671 ComObjPtr<Progress> progress;
672 try
673 {
674 switch (m->locInfo.storageType)
675 {
676 case VFSType_OCI:
677 rc = i_writeOCIImpl(m->locInfo, progress);
678 break;
679// case VFSType_GCP:
680// rc = i_writeGCPImpl(m->locInfo, progress);
681// break;
682// case VFSType_Amazon:
683// rc = i_writeAmazonImpl(m->locInfo, progress);
684// break;
685// case VFSType_Azure:
686// rc = i_writeAzureImpl(m->locInfo, progress);
687// break;
688 default:
689 break;
690 }
691
692 }
693 catch (HRESULT aRC)
694 {
695 rc = aRC;
696 }
697
698 if (SUCCEEDED(rc))
699 /* Return progress to the caller */
700 progress.queryInterfaceTo(aProgress.asOutParam());
701 }
702 else
703 {
704 m->fExportISOImages = m->optListExport.contains(ExportOptions_ExportDVDImages);
705
706 if (!m->fExportISOImages)/* remove all ISO images from VirtualSystemDescription */
707 {
708 for (list<ComObjPtr<VirtualSystemDescription> >::const_iterator
709 it = m->virtualSystemDescriptions.begin();
710 it != m->virtualSystemDescriptions.end();
711 ++it)
712 {
713 ComObjPtr<VirtualSystemDescription> vsdescThis = *it;
714 std::list<VirtualSystemDescriptionEntry*> skipped = vsdescThis->i_findByType(VirtualSystemDescriptionType_CDROM);
715 std::list<VirtualSystemDescriptionEntry*>::const_iterator itSkipped = skipped.begin();
716 while (itSkipped != skipped.end())
717 {
718 (*itSkipped)->skipIt = true;
719 ++itSkipped;
720 }
721 }
722 }
723
724 // do not allow entering this method if the appliance is busy reading or writing
725 if (!i_isApplianceIdle())
726 return E_ACCESSDENIED;
727
728 // figure the export format. We exploit the unknown version value for oracle public cloud.
729 ovf::OVFVersion_T ovfF;
730 if (aFormat == "ovf-0.9")
731 ovfF = ovf::OVFVersion_0_9;
732 else if (aFormat == "ovf-1.0")
733 ovfF = ovf::OVFVersion_1_0;
734 else if (aFormat == "ovf-2.0")
735 ovfF = ovf::OVFVersion_2_0;
736 else if (aFormat == "opc-1.0")
737 ovfF = ovf::OVFVersion_unknown;
738 else
739 return setError(VBOX_E_FILE_ERROR,
740 tr("Invalid format \"%s\" specified"), aFormat.c_str());
741
742 // Check the extension.
743 if (ovfF == ovf::OVFVersion_unknown)
744 {
745 if (!aPath.endsWith(".tar.gz", Utf8Str::CaseInsensitive))
746 return setError(VBOX_E_FILE_ERROR,
747 tr("OPC appliance file must have .tar.gz extension"));
748 }
749 else if ( !aPath.endsWith(".ovf", Utf8Str::CaseInsensitive)
750 && !aPath.endsWith(".ova", Utf8Str::CaseInsensitive))
751 return setError(VBOX_E_FILE_ERROR, tr("Appliance file must have .ovf or .ova extension"));
752
753
754 /* As of OVF 2.0 we have to use SHA-256 in the manifest. */
755 m->fManifest = m->optListExport.contains(ExportOptions_CreateManifest);
756 if (m->fManifest)
757 m->fDigestTypes = ovfF >= ovf::OVFVersion_2_0 ? RTMANIFEST_ATTR_SHA256 : RTMANIFEST_ATTR_SHA1;
758 Assert(m->hOurManifest == NIL_RTMANIFEST);
759
760 /* Check whether all passwords are supplied or error out. */
761 if (m->m_cPwProvided < m->m_vecPasswordIdentifiers.size())
762 return setError(VBOX_E_INVALID_OBJECT_STATE,
763 tr("Appliance export failed because not all passwords were provided for all encrypted media"));
764
765 ComObjPtr<Progress> progress;
766 rc = S_OK;
767 try
768 {
769 /* Parse all necessary info out of the URI */
770 i_parseURI(aPath, m->locInfo);
771
772 switch (ovfF)
773 {
774 case ovf::OVFVersion_unknown:
775 rc = i_writeOPCImpl(ovfF, m->locInfo, progress);
776 break;
777 default:
778 rc = i_writeImpl(ovfF, m->locInfo, progress);
779 break;
780 }
781
782 }
783 catch (HRESULT aRC)
784 {
785 rc = aRC;
786 }
787
788 if (SUCCEEDED(rc))
789 /* Return progress to the caller */
790 progress.queryInterfaceTo(aProgress.asOutParam());
791 }
792
793 return rc;
794}
795
796////////////////////////////////////////////////////////////////////////////////
797//
798// Appliance private methods
799//
800////////////////////////////////////////////////////////////////////////////////
801
802/*******************************************************************************
803 * Export stuff
804 ******************************************************************************/
805
806/**
807 * Implementation for writing out the OVF to disk. This starts a new thread which will call
808 * Appliance::taskThreadWriteOVF().
809 *
810 * This is in a separate private method because it is used from two locations:
811 *
812 * 1) from the public Appliance::Write().
813 *
814 * 2) in a second worker thread; in that case, Appliance::Write() called Appliance::i_writeImpl(), which
815 * called Appliance::i_writeFSOVA(), which called Appliance::i_writeImpl(), which then called this again.
816 *
817 * @param aFormat
818 * @param aLocInfo
819 * @param aProgress
820 * @return
821 */
822HRESULT Appliance::i_writeImpl(ovf::OVFVersion_T aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
823{
824 HRESULT rc;
825 try
826 {
827 rc = i_setUpProgress(aProgress,
828 BstrFmt(tr("Export appliance '%s'"), aLocInfo.strPath.c_str()),
829 (aLocInfo.storageType == VFSType_File) ? WriteFile : WriteS3);
830 if (FAILED(rc))
831 return rc;
832
833 /* Initialize our worker task */
834 TaskOVF* task = NULL;
835 try
836 {
837 task = new TaskOVF(this, TaskOVF::Write, aLocInfo, aProgress);
838 }
839 catch(...)
840 {
841 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
842 tr("Could not create TaskOVF object for for writing out the OVF to disk"));
843 }
844
845 /* The OVF version to write */
846 task->enFormat = aFormat;
847
848 rc = task->createThread();
849 if (FAILED(rc)) throw rc;
850
851 }
852 catch (HRESULT aRC)
853 {
854 rc = aRC;
855 }
856
857 return rc;
858}
859
860
861HRESULT Appliance::i_writeOCIImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
862{
863 HRESULT rc;
864 try
865 {
866 /** @todo r=klaus all of this code should be made fully generic,
867 * doing only what is common for all cloud providers, i.e. pretty
868 * much nothing. The logic and data types need to be moved to the
869 * extension pack, anything else will not work. */
870
871 //remove all disks from the VirtualSystemDescription exept one
872 for (list<ComObjPtr<VirtualSystemDescription> >::const_iterator
873 it = m->virtualSystemDescriptions.begin();
874 it != m->virtualSystemDescriptions.end();
875 ++it)
876 {
877 ComObjPtr<VirtualSystemDescription> vsdescThis = *it;
878 std::list<VirtualSystemDescriptionEntry*> skipped = vsdescThis->i_findByType(VirtualSystemDescriptionType_CDROM);
879 std::list<VirtualSystemDescriptionEntry*>::const_iterator itSkipped = skipped.begin();
880 while (itSkipped != skipped.end())
881 {
882 (*itSkipped)->skipIt = true;
883 ++itSkipped;
884 }
885
886 skipped = vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskImage);
887 itSkipped = skipped.begin();
888
889 Utf8Str strBootLocation;
890 while (itSkipped != skipped.end())
891 {
892 if (strBootLocation.isEmpty())
893 strBootLocation = (*itSkipped)->strVBoxCurrent;
894 else
895 (*itSkipped)->skipIt = true;
896 ++itSkipped;
897 }
898
899 //just in case
900 if (vsdescThis->i_findByType(VirtualSystemDescriptionType_HardDiskImage).empty())
901 {
902 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
903 tr("Strange, but nothing to export to OCI after preparation steps"));
904 }
905
906 /*
907 * Fills out the OCI settings
908 */
909 std::list<VirtualSystemDescriptionEntry*> machineName =
910 vsdescThis->i_findByType(VirtualSystemDescriptionType_Name);
911 if (machineName.empty())
912 throw setError(VBOX_E_FILE_ERROR, tr("OCI: VM name wasn't found"));
913 m->m_OciExportData.strDisplayMachineName = machineName.front()->strVBoxCurrent;
914 LogRel(("Exported machine name: %s\n", m->m_OciExportData.strDisplayMachineName.c_str()));
915
916 m->m_OciExportData.strBootImageName = strBootLocation;
917 LogRel(("Exported image: %s\n", m->m_OciExportData.strBootImageName.c_str()));
918
919 if (aLocInfo.strPath.isEmpty())
920 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
921 tr("OCI: Cloud user profile wasn't found"));
922
923 m->m_OciExportData.strProfileName = aLocInfo.strPath;
924 LogRel(("OCI profile name: %s\n", m->m_OciExportData.strProfileName.c_str()));
925
926 Utf8Str strInstanceShapeId;
927 std::list<VirtualSystemDescriptionEntry*> shapeId =
928 vsdescThis->i_findByType(VirtualSystemDescriptionType_CloudOCIInstanceShape);
929 if (shapeId.empty())
930 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
931 tr("OCI: Shape of instance wasn't found"));
932
933 m->m_OciExportData.strInstanceShapeId = shapeId.front()->strVBoxCurrent;
934 LogRel(("OCI shape: %s\n", m->m_OciExportData.strInstanceShapeId.c_str()));
935
936 std::list<VirtualSystemDescriptionEntry*> domainName =
937 vsdescThis->i_findByType(VirtualSystemDescriptionType_CloudOCIDomain);
938 if (domainName.empty())
939 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
940 tr("OCI: Available domain wasn't found"));
941
942 m->m_OciExportData.strDomainName = domainName.front()->strVBoxCurrent;
943 LogRel(("OCI available domain name: %s\n", m->m_OciExportData.strDomainName.c_str()));
944
945 std::list<VirtualSystemDescriptionEntry*> bootDiskSize =
946 vsdescThis->i_findByType(VirtualSystemDescriptionType_CloudOCIBootDiskSize);
947 if (bootDiskSize.empty())
948 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
949 tr("OCI: Boot disk size wasn't found"));
950
951 m->m_OciExportData.strBootDiskSize = bootDiskSize.front()->strVBoxCurrent;
952 LogRel(("OCI boot disk size: %s\n", m->m_OciExportData.strBootDiskSize.c_str()));
953
954 std::list<VirtualSystemDescriptionEntry*> bucketId =
955 vsdescThis->i_findByType(VirtualSystemDescriptionType_CloudOCIBucket);
956 if (bucketId.empty())
957 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
958 tr("OCI: Bucket wasn't found"));
959
960 m->m_OciExportData.strBucketId = bucketId.front()->strVBoxCurrent;
961 LogRel(("OCI bucket name: %s\n", m->m_OciExportData.strBucketId.c_str()));
962
963 std::list<VirtualSystemDescriptionEntry*> vcn =
964 vsdescThis->i_findByType(VirtualSystemDescriptionType_CloudOCIVCN);
965 if (vcn.empty())
966 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
967 tr("OCI: VCN wasn't found"));
968
969 m->m_OciExportData.strVCN = vcn.front()->strVBoxCurrent;
970 LogRel(("OCI VCN name: %s\n", m->m_OciExportData.strVCN.c_str()));
971
972 std::list<VirtualSystemDescriptionEntry*> publicIP =
973 vsdescThis->i_findByType(VirtualSystemDescriptionType_CloudOCIPublicIP);
974 if (publicIP.empty())
975 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
976 tr("OCI: Public IP setting wasn't found"));
977
978 m->m_OciExportData.fPublicIP = (publicIP.front()->strVBoxCurrent == "true") ? true : false;
979 LogRel(("OCI public IP: %s\n", m->m_OciExportData.fPublicIP ? "yes" : "no"));
980 }
981
982#ifndef VBOX_WITH_CLOUD_PROVIDERS_NO_COMMANDS
983 SetUpProgressMode mode = ExportOCI;
984
985 rc = i_setUpProgress(aProgress,
986 BstrFmt(tr("Export appliance to Cloud '%s'"), aLocInfo.strPath.c_str()),
987 mode);
988 if (FAILED(rc))
989 return rc;
990#else
991 // we need to do that as otherwise Task won't be created successfully
992 aProgress.createObject();
993#endif
994
995 // Initialize our worker task
996 TaskOCI* task = NULL;
997 try
998 {
999 task = new Appliance::TaskOCI(this, TaskOCI::Export, aLocInfo, aProgress);
1000 }
1001 catch(...)
1002 {
1003 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1004 tr("Could not create TaskOCI object for exporting to OCI"));
1005 }
1006
1007 rc = task->createThread();
1008 if (FAILED(rc)) throw rc;
1009
1010 }
1011 catch (HRESULT aRC)
1012 {
1013 rc = aRC;
1014 }
1015
1016 return rc;
1017}
1018
1019HRESULT Appliance::i_writeOPCImpl(ovf::OVFVersion_T aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
1020{
1021 HRESULT rc;
1022 RT_NOREF(aFormat);
1023 try
1024 {
1025 rc = i_setUpProgress(aProgress,
1026 BstrFmt(tr("Export appliance '%s'"), aLocInfo.strPath.c_str()),
1027 (aLocInfo.storageType == VFSType_File) ? WriteFile : WriteS3);
1028 if (FAILED(rc))
1029 return rc;
1030
1031 /* Initialize our worker task */
1032 TaskOPC* task = NULL;
1033 try
1034 {
1035 task = new Appliance::TaskOPC(this, TaskOPC::Export, aLocInfo, aProgress);
1036 }
1037 catch(...)
1038 {
1039 throw rc = setError(VBOX_E_OBJECT_NOT_FOUND,
1040 tr("Could not create TaskOPC object for for writing out the OPC to disk"));
1041 }
1042
1043 rc = task->createThread();
1044 if (FAILED(rc)) throw rc;
1045
1046 }
1047 catch (HRESULT aRC)
1048 {
1049 rc = aRC;
1050 }
1051
1052 return rc;
1053}
1054
1055
1056/**
1057 * Called from Appliance::i_writeFS() for creating a XML document for this
1058 * Appliance.
1059 *
1060 * @param writeLock The current write lock.
1061 * @param doc The xml document to fill.
1062 * @param stack Structure for temporary private
1063 * data shared with caller.
1064 * @param strPath Path to the target OVF.
1065 * instance for which to write XML.
1066 * @param enFormat OVF format (0.9 or 1.0).
1067 */
1068void Appliance::i_buildXML(AutoWriteLockBase& writeLock,
1069 xml::Document &doc,
1070 XMLStack &stack,
1071 const Utf8Str &strPath,
1072 ovf::OVFVersion_T enFormat)
1073{
1074 xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
1075
1076 pelmRoot->setAttribute("ovf:version", enFormat == ovf::OVFVersion_2_0 ? "2.0"
1077 : enFormat == ovf::OVFVersion_1_0 ? "1.0"
1078 : "0.9");
1079 pelmRoot->setAttribute("xml:lang", "en-US");
1080
1081 Utf8Str strNamespace;
1082
1083 if (enFormat == ovf::OVFVersion_0_9)
1084 {
1085 strNamespace = ovf::OVF09_URI_string;
1086 }
1087 else if (enFormat == ovf::OVFVersion_1_0)
1088 {
1089 strNamespace = ovf::OVF10_URI_string;
1090 }
1091 else
1092 {
1093 strNamespace = ovf::OVF20_URI_string;
1094 }
1095
1096 pelmRoot->setAttribute("xmlns", strNamespace);
1097 pelmRoot->setAttribute("xmlns:ovf", strNamespace);
1098
1099 // pelmRoot->setAttribute("xmlns:ovfstr", "http://schema.dmtf.org/ovf/strings/1");
1100 pelmRoot->setAttribute("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData");
1101 pelmRoot->setAttribute("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData");
1102 pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1103 pelmRoot->setAttribute("xmlns:vbox", "http://www.virtualbox.org/ovf/machine");
1104 // pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd");
1105
1106 if (enFormat == ovf::OVFVersion_2_0)
1107 {
1108 pelmRoot->setAttribute("xmlns:epasd",
1109 "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_EthernetPortAllocationSettingData.xsd");
1110 pelmRoot->setAttribute("xmlns:sasd",
1111 "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_StorageAllocationSettingData.xsd");
1112 }
1113
1114 // <Envelope>/<References>
1115 xml::ElementNode *pelmReferences = pelmRoot->createChild("References"); // 0.9 and 1.0
1116
1117 /* <Envelope>/<DiskSection>:
1118 <DiskSection>
1119 <Info>List of the virtual disks used in the package</Info>
1120 <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="..." ovf:populatedSize="1924967692"/>
1121 </DiskSection> */
1122 xml::ElementNode *pelmDiskSection;
1123 if (enFormat == ovf::OVFVersion_0_9)
1124 {
1125 // <Section xsi:type="ovf:DiskSection_Type">
1126 pelmDiskSection = pelmRoot->createChild("Section");
1127 pelmDiskSection->setAttribute("xsi:type", "ovf:DiskSection_Type");
1128 }
1129 else
1130 pelmDiskSection = pelmRoot->createChild("DiskSection");
1131
1132 xml::ElementNode *pelmDiskSectionInfo = pelmDiskSection->createChild("Info");
1133 pelmDiskSectionInfo->addContent("List of the virtual disks used in the package");
1134
1135 /* <Envelope>/<NetworkSection>:
1136 <NetworkSection>
1137 <Info>Logical networks used in the package</Info>
1138 <Network ovf:name="VM Network">
1139 <Description>The network that the LAMP Service will be available on</Description>
1140 </Network>
1141 </NetworkSection> */
1142 xml::ElementNode *pelmNetworkSection;
1143 if (enFormat == ovf::OVFVersion_0_9)
1144 {
1145 // <Section xsi:type="ovf:NetworkSection_Type">
1146 pelmNetworkSection = pelmRoot->createChild("Section");
1147 pelmNetworkSection->setAttribute("xsi:type", "ovf:NetworkSection_Type");
1148 }
1149 else
1150 pelmNetworkSection = pelmRoot->createChild("NetworkSection");
1151
1152 xml::ElementNode *pelmNetworkSectionInfo = pelmNetworkSection->createChild("Info");
1153 pelmNetworkSectionInfo->addContent("Logical networks used in the package");
1154
1155 // and here come the virtual systems:
1156
1157 // write a collection if we have more than one virtual system _and_ we're
1158 // writing OVF 1.0; otherwise fail since ovftool can't import more than
1159 // one machine, it seems
1160 xml::ElementNode *pelmToAddVirtualSystemsTo;
1161 if (m->virtualSystemDescriptions.size() > 1)
1162 {
1163 if (enFormat == ovf::OVFVersion_0_9)
1164 throw setError(VBOX_E_FILE_ERROR,
1165 tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0"));
1166
1167 pelmToAddVirtualSystemsTo = pelmRoot->createChild("VirtualSystemCollection");
1168 pelmToAddVirtualSystemsTo->setAttribute("ovf:name", "ExportedVirtualBoxMachines"); // whatever
1169 }
1170 else
1171 pelmToAddVirtualSystemsTo = pelmRoot; // add virtual system directly under root element
1172
1173 // this list receives pointers to the XML elements in the machine XML which
1174 // might have UUIDs that need fixing after we know the UUIDs of the exported images
1175 std::list<xml::ElementNode*> llElementsWithUuidAttributes;
1176 uint32_t ulFile = 1;
1177 /* Iterate through all virtual systems of that appliance */
1178 for (list<ComObjPtr<VirtualSystemDescription> >::const_iterator
1179 itV = m->virtualSystemDescriptions.begin();
1180 itV != m->virtualSystemDescriptions.end();
1181 ++itV)
1182 {
1183 ComObjPtr<VirtualSystemDescription> vsdescThis = *itV;
1184 i_buildXMLForOneVirtualSystem(writeLock,
1185 *pelmToAddVirtualSystemsTo,
1186 &llElementsWithUuidAttributes,
1187 vsdescThis,
1188 enFormat,
1189 stack); // disks and networks stack
1190
1191 list<Utf8Str> diskList;
1192
1193 for (list<Utf8Str>::const_iterator
1194 itDisk = stack.mapDiskSequenceForOneVM.begin();
1195 itDisk != stack.mapDiskSequenceForOneVM.end();
1196 ++itDisk)
1197 {
1198 const Utf8Str &strDiskID = *itDisk;
1199 const VirtualSystemDescriptionEntry *pDiskEntry = stack.mapDisks[strDiskID];
1200
1201 // source path: where the VBox image is
1202 const Utf8Str &strSrcFilePath = pDiskEntry->strVBoxCurrent;
1203 Bstr bstrSrcFilePath(strSrcFilePath);
1204
1205 //skip empty Medium. There are no information to add into section <References> or <DiskSection>
1206 if (strSrcFilePath.isEmpty() ||
1207 pDiskEntry->skipIt == true)
1208 continue;
1209
1210 // Do NOT check here whether the file exists. FindMedium will figure
1211 // that out, and filesystem-based tests are simply wrong in the
1212 // general case (think of iSCSI).
1213
1214 // We need some info from the source disks
1215 ComPtr<IMedium> pSourceDisk;
1216 //DeviceType_T deviceType = DeviceType_HardDisk;// by default
1217
1218 Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw()));
1219
1220 HRESULT rc;
1221
1222 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)
1223 {
1224 rc = mVirtualBox->OpenMedium(bstrSrcFilePath.raw(),
1225 DeviceType_HardDisk,
1226 AccessMode_ReadWrite,
1227 FALSE /* fForceNewUuid */,
1228 pSourceDisk.asOutParam());
1229 if (FAILED(rc))
1230 throw rc;
1231 }
1232 else if (pDiskEntry->type == VirtualSystemDescriptionType_CDROM)//may be, this is CD/DVD
1233 {
1234 rc = mVirtualBox->OpenMedium(bstrSrcFilePath.raw(),
1235 DeviceType_DVD,
1236 AccessMode_ReadOnly,
1237 FALSE,
1238 pSourceDisk.asOutParam());
1239 if (FAILED(rc))
1240 throw rc;
1241 }
1242
1243 Bstr uuidSource;
1244 rc = pSourceDisk->COMGETTER(Id)(uuidSource.asOutParam());
1245 if (FAILED(rc)) throw rc;
1246 Guid guidSource(uuidSource);
1247
1248 // output filename
1249 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
1250
1251 // target path needs to be composed from where the output OVF is
1252 Utf8Str strTargetFilePath(strPath);
1253 strTargetFilePath.stripFilename();
1254 strTargetFilePath.append("/");
1255 strTargetFilePath.append(strTargetFileNameOnly);
1256
1257 // We are always exporting to VMDK stream optimized for now
1258 //Bstr bstrSrcFormat = L"VMDK";//not used
1259
1260 diskList.push_back(strTargetFilePath);
1261
1262 LONG64 cbCapacity = 0; // size reported to guest
1263 rc = pSourceDisk->COMGETTER(LogicalSize)(&cbCapacity);
1264 if (FAILED(rc)) throw rc;
1265 /// @todo r=poetzsch: wrong it is reported in bytes ...
1266 // capacity is reported in megabytes, so...
1267 //cbCapacity *= _1M;
1268
1269 Guid guidTarget; /* Creates a new uniq number for the target disk. */
1270 guidTarget.create();
1271
1272 // now handle the XML for the disk:
1273 Utf8StrFmt strFileRef("file%RI32", ulFile++);
1274 // <File ovf:href="WindowsXpProfessional-disk1.vmdk" ovf:id="file1" ovf:size="1710381056"/>
1275 xml::ElementNode *pelmFile = pelmReferences->createChild("File");
1276 pelmFile->setAttribute("ovf:id", strFileRef);
1277 pelmFile->setAttribute("ovf:href", strTargetFileNameOnly);
1278 /// @todo the actual size is not available at this point of time,
1279 // cause the disk will be compressed. The 1.0 standard says this is
1280 // optional! 1.1 isn't fully clear if the "gzip" format is used.
1281 // Need to be checked. */
1282 // pelmFile->setAttribute("ovf:size", Utf8StrFmt("%RI64", cbFile).c_str());
1283
1284 // add disk to XML Disks section
1285 // <Disk ovf:capacity="8589934592" ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:format="..."/>
1286 xml::ElementNode *pelmDisk = pelmDiskSection->createChild("Disk");
1287 pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str());
1288 pelmDisk->setAttribute("ovf:diskId", strDiskID);
1289 pelmDisk->setAttribute("ovf:fileRef", strFileRef);
1290
1291 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)//deviceType == DeviceType_HardDisk
1292 {
1293 pelmDisk->setAttribute("ovf:format",
1294 (enFormat == ovf::OVFVersion_0_9)
1295 ? "http://www.vmware.com/specifications/vmdk.html#sparse" // must be sparse or ovftoo
1296 : "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"
1297 // correct string as communicated to us by VMware (public bug #6612)
1298 );
1299 }
1300 else //pDiskEntry->type == VirtualSystemDescriptionType_CDROM, deviceType == DeviceType_DVD
1301 {
1302 pelmDisk->setAttribute("ovf:format",
1303 "http://www.ecma-international.org/publications/standards/Ecma-119.htm"
1304 );
1305 }
1306
1307 // add the UUID of the newly target image to the OVF disk element, but in the
1308 // vbox: namespace since it's not part of the standard
1309 pelmDisk->setAttribute("vbox:uuid", Utf8StrFmt("%RTuuid", guidTarget.raw()).c_str());
1310
1311 // now, we might have other XML elements from vbox:Machine pointing to this image,
1312 // but those would refer to the UUID of the _source_ image (which we created the
1313 // export image from); those UUIDs need to be fixed to the export image
1314 Utf8Str strGuidSourceCurly = guidSource.toStringCurly();
1315 for (std::list<xml::ElementNode*>::const_iterator
1316 it = llElementsWithUuidAttributes.begin();
1317 it != llElementsWithUuidAttributes.end();
1318 ++it)
1319 {
1320 xml::ElementNode *pelmImage = *it;
1321 Utf8Str strUUID;
1322 pelmImage->getAttributeValue("uuid", strUUID);
1323 if (strUUID == strGuidSourceCurly)
1324 // overwrite existing uuid attribute
1325 pelmImage->setAttribute("uuid", guidTarget.toStringCurly());
1326 }
1327 }
1328 llElementsWithUuidAttributes.clear();
1329 stack.mapDiskSequenceForOneVM.clear();
1330 }
1331
1332 // now, fill in the network section we set up empty above according
1333 // to the networks we found with the hardware items
1334 for (map<Utf8Str, bool>::const_iterator
1335 it = stack.mapNetworks.begin();
1336 it != stack.mapNetworks.end();
1337 ++it)
1338 {
1339 const Utf8Str &strNetwork = it->first;
1340 xml::ElementNode *pelmNetwork = pelmNetworkSection->createChild("Network");
1341 pelmNetwork->setAttribute("ovf:name", strNetwork.c_str());
1342 pelmNetwork->createChild("Description")->addContent("Logical network used by this appliance.");
1343 }
1344
1345}
1346
1347/**
1348 * Called from Appliance::i_buildXML() for each virtual system (machine) that
1349 * needs XML written out.
1350 *
1351 * @param writeLock The current write lock.
1352 * @param elmToAddVirtualSystemsTo XML element to append elements to.
1353 * @param pllElementsWithUuidAttributes out: list of XML elements produced here
1354 * with UUID attributes for quick
1355 * fixing by caller later
1356 * @param vsdescThis The IVirtualSystemDescription
1357 * instance for which to write XML.
1358 * @param enFormat OVF format (0.9 or 1.0).
1359 * @param stack Structure for temporary private
1360 * data shared with caller.
1361 */
1362void Appliance::i_buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock,
1363 xml::ElementNode &elmToAddVirtualSystemsTo,
1364 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes,
1365 ComObjPtr<VirtualSystemDescription> &vsdescThis,
1366 ovf::OVFVersion_T enFormat,
1367 XMLStack &stack)
1368{
1369 LogFlowFunc(("ENTER appliance %p\n", this));
1370
1371 xml::ElementNode *pelmVirtualSystem;
1372 if (enFormat == ovf::OVFVersion_0_9)
1373 {
1374 // <Section xsi:type="ovf:NetworkSection_Type">
1375 pelmVirtualSystem = elmToAddVirtualSystemsTo.createChild("Content");
1376 pelmVirtualSystem->setAttribute("xsi:type", "ovf:VirtualSystem_Type");
1377 }
1378 else
1379 pelmVirtualSystem = elmToAddVirtualSystemsTo.createChild("VirtualSystem");
1380
1381 /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine");
1382
1383 std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->i_findByType(VirtualSystemDescriptionType_Name);
1384 if (llName.empty())
1385 throw setError(VBOX_E_NOT_SUPPORTED, tr("Missing VM name"));
1386 Utf8Str &strVMName = llName.back()->strVBoxCurrent;
1387 pelmVirtualSystem->setAttribute("ovf:id", strVMName);
1388
1389 // product info
1390 std::list<VirtualSystemDescriptionEntry*> llProduct = vsdescThis->i_findByType(VirtualSystemDescriptionType_Product);
1391 std::list<VirtualSystemDescriptionEntry*> llProductUrl = vsdescThis->i_findByType(VirtualSystemDescriptionType_ProductUrl);
1392 std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->i_findByType(VirtualSystemDescriptionType_Vendor);
1393 std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->i_findByType(VirtualSystemDescriptionType_VendorUrl);
1394 std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->i_findByType(VirtualSystemDescriptionType_Version);
1395 bool fProduct = llProduct.size() && !llProduct.back()->strVBoxCurrent.isEmpty();
1396 bool fProductUrl = llProductUrl.size() && !llProductUrl.back()->strVBoxCurrent.isEmpty();
1397 bool fVendor = llVendor.size() && !llVendor.back()->strVBoxCurrent.isEmpty();
1398 bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.back()->strVBoxCurrent.isEmpty();
1399 bool fVersion = llVersion.size() && !llVersion.back()->strVBoxCurrent.isEmpty();
1400 if (fProduct || fProductUrl || fVendor || fVendorUrl || fVersion)
1401 {
1402 /* <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
1403 <Info>Meta-information about the installed software</Info>
1404 <Product>VAtest</Product>
1405 <Vendor>SUN Microsystems</Vendor>
1406 <Version>10.0</Version>
1407 <ProductUrl>http://blogs.sun.com/VirtualGuru</ProductUrl>
1408 <VendorUrl>http://www.sun.com</VendorUrl>
1409 </Section> */
1410 xml::ElementNode *pelmAnnotationSection;
1411 if (enFormat == ovf::OVFVersion_0_9)
1412 {
1413 // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type">
1414 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
1415 pelmAnnotationSection->setAttribute("xsi:type", "ovf:ProductSection_Type");
1416 }
1417 else
1418 pelmAnnotationSection = pelmVirtualSystem->createChild("ProductSection");
1419
1420 pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software");
1421 if (fProduct)
1422 pelmAnnotationSection->createChild("Product")->addContent(llProduct.back()->strVBoxCurrent);
1423 if (fVendor)
1424 pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.back()->strVBoxCurrent);
1425 if (fVersion)
1426 pelmAnnotationSection->createChild("Version")->addContent(llVersion.back()->strVBoxCurrent);
1427 if (fProductUrl)
1428 pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.back()->strVBoxCurrent);
1429 if (fVendorUrl)
1430 pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.back()->strVBoxCurrent);
1431 }
1432
1433 // description
1434 std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->i_findByType(VirtualSystemDescriptionType_Description);
1435 if (llDescription.size() &&
1436 !llDescription.back()->strVBoxCurrent.isEmpty())
1437 {
1438 /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
1439 <Info>A human-readable annotation</Info>
1440 <Annotation>Plan 9</Annotation>
1441 </Section> */
1442 xml::ElementNode *pelmAnnotationSection;
1443 if (enFormat == ovf::OVFVersion_0_9)
1444 {
1445 // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type">
1446 pelmAnnotationSection = pelmVirtualSystem->createChild("Section");
1447 pelmAnnotationSection->setAttribute("xsi:type", "ovf:AnnotationSection_Type");
1448 }
1449 else
1450 pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection");
1451
1452 pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation");
1453 pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.back()->strVBoxCurrent);
1454 }
1455
1456 // license
1457 std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->i_findByType(VirtualSystemDescriptionType_License);
1458 if (llLicense.size() &&
1459 !llLicense.back()->strVBoxCurrent.isEmpty())
1460 {
1461 /* <EulaSection>
1462 <Info ovf:msgid="6">License agreement for the Virtual System.</Info>
1463 <License ovf:msgid="1">License terms can go in here.</License>
1464 </EulaSection> */
1465 xml::ElementNode *pelmEulaSection;
1466 if (enFormat == ovf::OVFVersion_0_9)
1467 {
1468 pelmEulaSection = pelmVirtualSystem->createChild("Section");
1469 pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type");
1470 }
1471 else
1472 pelmEulaSection = pelmVirtualSystem->createChild("EulaSection");
1473
1474 pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system");
1475 pelmEulaSection->createChild("License")->addContent(llLicense.back()->strVBoxCurrent);
1476 }
1477
1478 // operating system
1479 std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->i_findByType(VirtualSystemDescriptionType_OS);
1480 if (llOS.empty())
1481 throw setError(VBOX_E_NOT_SUPPORTED, tr("Missing OS type"));
1482 /* <OperatingSystemSection ovf:id="82">
1483 <Info>Guest Operating System</Info>
1484 <Description>Linux 2.6.x</Description>
1485 </OperatingSystemSection> */
1486 VirtualSystemDescriptionEntry *pvsdeOS = llOS.back();
1487 xml::ElementNode *pelmOperatingSystemSection;
1488 if (enFormat == ovf::OVFVersion_0_9)
1489 {
1490 pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section");
1491 pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type");
1492 }
1493 else
1494 pelmOperatingSystemSection = pelmVirtualSystem->createChild("OperatingSystemSection");
1495
1496 pelmOperatingSystemSection->setAttribute("ovf:id", pvsdeOS->strOvf);
1497 pelmOperatingSystemSection->createChild("Info")->addContent("The kind of installed guest operating system");
1498 Utf8Str strOSDesc;
1499 convertCIMOSType2VBoxOSType(strOSDesc, (ovf::CIMOSType_T)pvsdeOS->strOvf.toInt32(), "");
1500 pelmOperatingSystemSection->createChild("Description")->addContent(strOSDesc);
1501 // add the VirtualBox ostype in a custom tag in a different namespace
1502 xml::ElementNode *pelmVBoxOSType = pelmOperatingSystemSection->createChild("vbox:OSType");
1503 pelmVBoxOSType->setAttribute("ovf:required", "false");
1504 pelmVBoxOSType->addContent(pvsdeOS->strVBoxCurrent);
1505
1506 // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso">
1507 xml::ElementNode *pelmVirtualHardwareSection;
1508 if (enFormat == ovf::OVFVersion_0_9)
1509 {
1510 // <Section xsi:type="ovf:VirtualHardwareSection_Type">
1511 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section");
1512 pelmVirtualHardwareSection->setAttribute("xsi:type", "ovf:VirtualHardwareSection_Type");
1513 }
1514 else
1515 pelmVirtualHardwareSection = pelmVirtualSystem->createChild("VirtualHardwareSection");
1516
1517 pelmVirtualHardwareSection->createChild("Info")->addContent("Virtual hardware requirements for a virtual machine");
1518
1519 /* <System>
1520 <vssd:Description>Description of the virtual hardware section.</vssd:Description>
1521 <vssd:ElementName>vmware</vssd:ElementName>
1522 <vssd:InstanceID>1</vssd:InstanceID>
1523 <vssd:VirtualSystemIdentifier>MyLampService</vssd:VirtualSystemIdentifier>
1524 <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
1525 </System> */
1526 xml::ElementNode *pelmSystem = pelmVirtualHardwareSection->createChild("System");
1527
1528 pelmSystem->createChild("vssd:ElementName")->addContent("Virtual Hardware Family"); // required OVF 1.0
1529
1530 // <vssd:InstanceId>0</vssd:InstanceId>
1531 if (enFormat == ovf::OVFVersion_0_9)
1532 pelmSystem->createChild("vssd:InstanceId")->addContent("0");
1533 else // capitalization changed...
1534 pelmSystem->createChild("vssd:InstanceID")->addContent("0");
1535
1536 // <vssd:VirtualSystemIdentifier>VAtest</vssd:VirtualSystemIdentifier>
1537 pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName);
1538 // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType>
1539 const char *pcszHardware = "virtualbox-2.2";
1540 if (enFormat == ovf::OVFVersion_0_9)
1541 // pretend to be vmware compatible then
1542 pcszHardware = "vmx-6";
1543 pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware);
1544
1545 // loop thru all description entries twice; once to write out all
1546 // devices _except_ disk images, and a second time to assign the
1547 // disk images; this is because disk images need to reference
1548 // IDE controllers, and we can't know their instance IDs without
1549 // assigning them first
1550
1551 uint32_t idIDEPrimaryController = 0;
1552 int32_t lIDEPrimaryControllerIndex = 0;
1553 uint32_t idIDESecondaryController = 0;
1554 int32_t lIDESecondaryControllerIndex = 0;
1555 uint32_t idSATAController = 0;
1556 int32_t lSATAControllerIndex = 0;
1557 uint32_t idSCSIController = 0;
1558 int32_t lSCSIControllerIndex = 0;
1559
1560 uint32_t ulInstanceID = 1;
1561
1562 uint32_t cDVDs = 0;
1563
1564 for (size_t uLoop = 1; uLoop <= 2; ++uLoop)
1565 {
1566 int32_t lIndexThis = 0;
1567 for (vector<VirtualSystemDescriptionEntry>::const_iterator
1568 it = vsdescThis->m->maDescriptions.begin();
1569 it != vsdescThis->m->maDescriptions.end();
1570 ++it, ++lIndexThis)
1571 {
1572 const VirtualSystemDescriptionEntry &desc = *it;
1573
1574 LogFlowFunc(("Loop %u: handling description entry ulIndex=%u, type=%s, strRef=%s, strOvf=%s, strVBox=%s, strExtraConfig=%s\n",
1575 uLoop,
1576 desc.ulIndex,
1577 ( desc.type == VirtualSystemDescriptionType_HardDiskControllerIDE ? "HardDiskControllerIDE"
1578 : desc.type == VirtualSystemDescriptionType_HardDiskControllerSATA ? "HardDiskControllerSATA"
1579 : desc.type == VirtualSystemDescriptionType_HardDiskControllerSCSI ? "HardDiskControllerSCSI"
1580 : desc.type == VirtualSystemDescriptionType_HardDiskControllerSAS ? "HardDiskControllerSAS"
1581 : desc.type == VirtualSystemDescriptionType_HardDiskImage ? "HardDiskImage"
1582 : Utf8StrFmt("%d", desc.type).c_str()),
1583 desc.strRef.c_str(),
1584 desc.strOvf.c_str(),
1585 desc.strVBoxCurrent.c_str(),
1586 desc.strExtraConfigCurrent.c_str()));
1587
1588 ovf::ResourceType_T type = (ovf::ResourceType_T)0; // if this becomes != 0 then we do stuff
1589 Utf8Str strResourceSubType;
1590
1591 Utf8Str strDescription; // results in <rasd:Description>...</rasd:Description> block
1592 Utf8Str strCaption; // results in <rasd:Caption>...</rasd:Caption> block
1593
1594 uint32_t ulParent = 0;
1595
1596 int32_t lVirtualQuantity = -1;
1597 Utf8Str strAllocationUnits;
1598
1599 int32_t lAddress = -1;
1600 int32_t lBusNumber = -1;
1601 int32_t lAddressOnParent = -1;
1602
1603 int32_t lAutomaticAllocation = -1; // 0 means "false", 1 means "true"
1604 Utf8Str strConnection; // results in <rasd:Connection>...</rasd:Connection> block
1605 Utf8Str strHostResource;
1606
1607 uint64_t uTemp;
1608
1609 ovf::VirtualHardwareItem vhi;
1610 ovf::StorageItem si;
1611 ovf::EthernetPortItem epi;
1612
1613 switch (desc.type)
1614 {
1615 case VirtualSystemDescriptionType_CPU:
1616 /* <Item>
1617 <rasd:Caption>1 virtual CPU</rasd:Caption>
1618 <rasd:Description>Number of virtual CPUs</rasd:Description>
1619 <rasd:ElementName>virtual CPU</rasd:ElementName>
1620 <rasd:InstanceID>1</rasd:InstanceID>
1621 <rasd:ResourceType>3</rasd:ResourceType>
1622 <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
1623 </Item> */
1624 if (uLoop == 1)
1625 {
1626 strDescription = "Number of virtual CPUs";
1627 type = ovf::ResourceType_Processor; // 3
1628 desc.strVBoxCurrent.toInt(uTemp);
1629 lVirtualQuantity = (int32_t)uTemp;
1630 strCaption = Utf8StrFmt("%d virtual CPU", lVirtualQuantity); // without this ovftool
1631 // won't eat the item
1632 }
1633 break;
1634
1635 case VirtualSystemDescriptionType_Memory:
1636 /* <Item>
1637 <rasd:AllocationUnits>MegaBytes</rasd:AllocationUnits>
1638 <rasd:Caption>256 MB of memory</rasd:Caption>
1639 <rasd:Description>Memory Size</rasd:Description>
1640 <rasd:ElementName>Memory</rasd:ElementName>
1641 <rasd:InstanceID>2</rasd:InstanceID>
1642 <rasd:ResourceType>4</rasd:ResourceType>
1643 <rasd:VirtualQuantity>256</rasd:VirtualQuantity>
1644 </Item> */
1645 if (uLoop == 1)
1646 {
1647 strDescription = "Memory Size";
1648 type = ovf::ResourceType_Memory; // 4
1649 desc.strVBoxCurrent.toInt(uTemp);
1650 lVirtualQuantity = (int32_t)(uTemp / _1M);
1651 strAllocationUnits = "MegaBytes";
1652 strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity); // without this ovftool
1653 // won't eat the item
1654 }
1655 break;
1656
1657 case VirtualSystemDescriptionType_HardDiskControllerIDE:
1658 /* <Item>
1659 <rasd:Caption>ideController1</rasd:Caption>
1660 <rasd:Description>IDE Controller</rasd:Description>
1661 <rasd:InstanceId>5</rasd:InstanceId>
1662 <rasd:ResourceType>5</rasd:ResourceType>
1663 <rasd:Address>1</rasd:Address>
1664 <rasd:BusNumber>1</rasd:BusNumber>
1665 </Item> */
1666 if (uLoop == 1)
1667 {
1668 strDescription = "IDE Controller";
1669 type = ovf::ResourceType_IDEController; // 5
1670 strResourceSubType = desc.strVBoxCurrent;
1671
1672 if (!lIDEPrimaryControllerIndex)
1673 {
1674 // first IDE controller:
1675 strCaption = "ideController0";
1676 lAddress = 0;
1677 lBusNumber = 0;
1678 // remember this ID
1679 idIDEPrimaryController = ulInstanceID;
1680 lIDEPrimaryControllerIndex = lIndexThis;
1681 }
1682 else
1683 {
1684 // second IDE controller:
1685 strCaption = "ideController1";
1686 lAddress = 1;
1687 lBusNumber = 1;
1688 // remember this ID
1689 idIDESecondaryController = ulInstanceID;
1690 lIDESecondaryControllerIndex = lIndexThis;
1691 }
1692 }
1693 break;
1694
1695 case VirtualSystemDescriptionType_HardDiskControllerSATA:
1696 /* <Item>
1697 <rasd:Caption>sataController0</rasd:Caption>
1698 <rasd:Description>SATA Controller</rasd:Description>
1699 <rasd:InstanceId>4</rasd:InstanceId>
1700 <rasd:ResourceType>20</rasd:ResourceType>
1701 <rasd:ResourceSubType>ahci</rasd:ResourceSubType>
1702 <rasd:Address>0</rasd:Address>
1703 <rasd:BusNumber>0</rasd:BusNumber>
1704 </Item>
1705 */
1706 if (uLoop == 1)
1707 {
1708 strDescription = "SATA Controller";
1709 strCaption = "sataController0";
1710 type = ovf::ResourceType_OtherStorageDevice; // 20
1711 // it seems that OVFTool always writes these two, and since we can only
1712 // have one SATA controller, we'll use this as well
1713 lAddress = 0;
1714 lBusNumber = 0;
1715
1716 if ( desc.strVBoxCurrent.isEmpty() // AHCI is the default in VirtualBox
1717 || (!desc.strVBoxCurrent.compare("ahci", Utf8Str::CaseInsensitive))
1718 )
1719 strResourceSubType = "AHCI";
1720 else
1721 throw setError(VBOX_E_NOT_SUPPORTED,
1722 tr("Invalid config string \"%s\" in SATA controller"), desc.strVBoxCurrent.c_str());
1723
1724 // remember this ID
1725 idSATAController = ulInstanceID;
1726 lSATAControllerIndex = lIndexThis;
1727 }
1728 break;
1729
1730 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
1731 case VirtualSystemDescriptionType_HardDiskControllerSAS:
1732 /* <Item>
1733 <rasd:Caption>scsiController0</rasd:Caption>
1734 <rasd:Description>SCSI Controller</rasd:Description>
1735 <rasd:InstanceId>4</rasd:InstanceId>
1736 <rasd:ResourceType>6</rasd:ResourceType>
1737 <rasd:ResourceSubType>buslogic</rasd:ResourceSubType>
1738 <rasd:Address>0</rasd:Address>
1739 <rasd:BusNumber>0</rasd:BusNumber>
1740 </Item>
1741 */
1742 if (uLoop == 1)
1743 {
1744 strDescription = "SCSI Controller";
1745 strCaption = "scsiController0";
1746 type = ovf::ResourceType_ParallelSCSIHBA; // 6
1747 // it seems that OVFTool always writes these two, and since we can only
1748 // have one SATA controller, we'll use this as well
1749 lAddress = 0;
1750 lBusNumber = 0;
1751
1752 if ( desc.strVBoxCurrent.isEmpty() // LsiLogic is the default in VirtualBox
1753 || (!desc.strVBoxCurrent.compare("lsilogic", Utf8Str::CaseInsensitive))
1754 )
1755 strResourceSubType = "lsilogic";
1756 else if (!desc.strVBoxCurrent.compare("buslogic", Utf8Str::CaseInsensitive))
1757 strResourceSubType = "buslogic";
1758 else if (!desc.strVBoxCurrent.compare("lsilogicsas", Utf8Str::CaseInsensitive))
1759 strResourceSubType = "lsilogicsas";
1760 else
1761 throw setError(VBOX_E_NOT_SUPPORTED,
1762 tr("Invalid config string \"%s\" in SCSI/SAS controller"),
1763 desc.strVBoxCurrent.c_str());
1764
1765 // remember this ID
1766 idSCSIController = ulInstanceID;
1767 lSCSIControllerIndex = lIndexThis;
1768 }
1769 break;
1770
1771 case VirtualSystemDescriptionType_HardDiskImage:
1772 /* <Item>
1773 <rasd:Caption>disk1</rasd:Caption>
1774 <rasd:InstanceId>8</rasd:InstanceId>
1775 <rasd:ResourceType>17</rasd:ResourceType>
1776 <rasd:HostResource>/disk/vmdisk1</rasd:HostResource>
1777 <rasd:Parent>4</rasd:Parent>
1778 <rasd:AddressOnParent>0</rasd:AddressOnParent>
1779 </Item> */
1780 if (uLoop == 2)
1781 {
1782 uint32_t cDisks = (uint32_t)stack.mapDisks.size();
1783 Utf8Str strDiskID = Utf8StrFmt("vmdisk%RI32", ++cDisks);
1784
1785 strDescription = "Disk Image";
1786 strCaption = Utf8StrFmt("disk%RI32", cDisks); // this is not used for anything else
1787 type = ovf::ResourceType_HardDisk; // 17
1788
1789 // the following references the "<Disks>" XML block
1790 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
1791
1792 // controller=<index>;channel=<c>
1793 size_t pos1 = desc.strExtraConfigCurrent.find("controller=");
1794 size_t pos2 = desc.strExtraConfigCurrent.find("channel=");
1795 int32_t lControllerIndex = -1;
1796 if (pos1 != Utf8Str::npos)
1797 {
1798 RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
1799 if (lControllerIndex == lIDEPrimaryControllerIndex)
1800 ulParent = idIDEPrimaryController;
1801 else if (lControllerIndex == lIDESecondaryControllerIndex)
1802 ulParent = idIDESecondaryController;
1803 else if (lControllerIndex == lSCSIControllerIndex)
1804 ulParent = idSCSIController;
1805 else if (lControllerIndex == lSATAControllerIndex)
1806 ulParent = idSATAController;
1807 }
1808 if (pos2 != Utf8Str::npos)
1809 RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
1810
1811 LogFlowFunc(("HardDiskImage details: pos1=%d, pos2=%d, lControllerIndex=%d, lIDEPrimaryControllerIndex=%d, lIDESecondaryControllerIndex=%d, ulParent=%d, lAddressOnParent=%d\n",
1812 pos1, pos2, lControllerIndex, lIDEPrimaryControllerIndex, lIDESecondaryControllerIndex,
1813 ulParent, lAddressOnParent));
1814
1815 if ( !ulParent
1816 || lAddressOnParent == -1
1817 )
1818 throw setError(VBOX_E_NOT_SUPPORTED,
1819 tr("Missing or bad extra config string in hard disk image: \"%s\""),
1820 desc.strExtraConfigCurrent.c_str());
1821
1822 stack.mapDisks[strDiskID] = &desc;
1823
1824 //use the list stack.mapDiskSequence where the disks go as the "VirtualSystem" should be placed
1825 //in the OVF description file.
1826 stack.mapDiskSequence.push_back(strDiskID);
1827 stack.mapDiskSequenceForOneVM.push_back(strDiskID);
1828 }
1829 break;
1830
1831 case VirtualSystemDescriptionType_Floppy:
1832 if (uLoop == 1)
1833 {
1834 strDescription = "Floppy Drive";
1835 strCaption = "floppy0"; // this is what OVFTool writes
1836 type = ovf::ResourceType_FloppyDrive; // 14
1837 lAutomaticAllocation = 0;
1838 lAddressOnParent = 0; // this is what OVFTool writes
1839 }
1840 break;
1841
1842 case VirtualSystemDescriptionType_CDROM:
1843 /* <Item>
1844 <rasd:Caption>cdrom1</rasd:Caption>
1845 <rasd:InstanceId>8</rasd:InstanceId>
1846 <rasd:ResourceType>15</rasd:ResourceType>
1847 <rasd:HostResource>/disk/cdrom1</rasd:HostResource>
1848 <rasd:Parent>4</rasd:Parent>
1849 <rasd:AddressOnParent>0</rasd:AddressOnParent>
1850 </Item> */
1851 if (uLoop == 2)
1852 {
1853 uint32_t cDisks = (uint32_t)stack.mapDisks.size();
1854 Utf8Str strDiskID = Utf8StrFmt("iso%RI32", ++cDisks);
1855 ++cDVDs;
1856 strDescription = "CD-ROM Drive";
1857 strCaption = Utf8StrFmt("cdrom%RI32", cDVDs); // OVFTool starts with 1
1858 type = ovf::ResourceType_CDDrive; // 15
1859 lAutomaticAllocation = 1;
1860
1861 //skip empty Medium. There are no information to add into section <References> or <DiskSection>
1862 if (desc.strVBoxCurrent.isNotEmpty() &&
1863 desc.skipIt == false)
1864 {
1865 // the following references the "<Disks>" XML block
1866 strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str());
1867 }
1868
1869 // controller=<index>;channel=<c>
1870 size_t pos1 = desc.strExtraConfigCurrent.find("controller=");
1871 size_t pos2 = desc.strExtraConfigCurrent.find("channel=");
1872 int32_t lControllerIndex = -1;
1873 if (pos1 != Utf8Str::npos)
1874 {
1875 RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos1 + 11, NULL, 0, &lControllerIndex);
1876 if (lControllerIndex == lIDEPrimaryControllerIndex)
1877 ulParent = idIDEPrimaryController;
1878 else if (lControllerIndex == lIDESecondaryControllerIndex)
1879 ulParent = idIDESecondaryController;
1880 else if (lControllerIndex == lSCSIControllerIndex)
1881 ulParent = idSCSIController;
1882 else if (lControllerIndex == lSATAControllerIndex)
1883 ulParent = idSATAController;
1884 }
1885 if (pos2 != Utf8Str::npos)
1886 RTStrToInt32Ex(desc.strExtraConfigCurrent.c_str() + pos2 + 8, NULL, 0, &lAddressOnParent);
1887
1888 LogFlowFunc(("DVD drive details: pos1=%d, pos2=%d, lControllerIndex=%d, lIDEPrimaryControllerIndex=%d, lIDESecondaryControllerIndex=%d, ulParent=%d, lAddressOnParent=%d\n",
1889 pos1, pos2, lControllerIndex, lIDEPrimaryControllerIndex,
1890 lIDESecondaryControllerIndex, ulParent, lAddressOnParent));
1891
1892 if ( !ulParent
1893 || lAddressOnParent == -1
1894 )
1895 throw setError(VBOX_E_NOT_SUPPORTED,
1896 tr("Missing or bad extra config string in DVD drive medium: \"%s\""),
1897 desc.strExtraConfigCurrent.c_str());
1898
1899 stack.mapDisks[strDiskID] = &desc;
1900
1901 //use the list stack.mapDiskSequence where the disks go as the "VirtualSystem" should be placed
1902 //in the OVF description file.
1903 stack.mapDiskSequence.push_back(strDiskID);
1904 stack.mapDiskSequenceForOneVM.push_back(strDiskID);
1905 // there is no DVD drive map to update because it is
1906 // handled completely with this entry.
1907 }
1908 break;
1909
1910 case VirtualSystemDescriptionType_NetworkAdapter:
1911 /* <Item>
1912 <rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
1913 <rasd:Caption>Ethernet adapter on 'VM Network'</rasd:Caption>
1914 <rasd:Connection>VM Network</rasd:Connection>
1915 <rasd:ElementName>VM network</rasd:ElementName>
1916 <rasd:InstanceID>3</rasd:InstanceID>
1917 <rasd:ResourceType>10</rasd:ResourceType>
1918 </Item> */
1919 if (uLoop == 2)
1920 {
1921 lAutomaticAllocation = 1;
1922 strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str());
1923 type = ovf::ResourceType_EthernetAdapter; // 10
1924 /* Set the hardware type to something useful.
1925 * To be compatible with vmware & others we set
1926 * PCNet32 for our PCNet types & E1000 for the
1927 * E1000 cards. */
1928 switch (desc.strVBoxCurrent.toInt32())
1929 {
1930 case NetworkAdapterType_Am79C970A:
1931 case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break;
1932#ifdef VBOX_WITH_E1000
1933 case NetworkAdapterType_I82540EM:
1934 case NetworkAdapterType_I82545EM:
1935 case NetworkAdapterType_I82543GC: strResourceSubType = "E1000"; break;
1936#endif /* VBOX_WITH_E1000 */
1937 }
1938 strConnection = desc.strOvf;
1939
1940 stack.mapNetworks[desc.strOvf] = true;
1941 }
1942 break;
1943
1944 case VirtualSystemDescriptionType_USBController:
1945 /* <Item ovf:required="false">
1946 <rasd:Caption>usb</rasd:Caption>
1947 <rasd:Description>USB Controller</rasd:Description>
1948 <rasd:InstanceId>3</rasd:InstanceId>
1949 <rasd:ResourceType>23</rasd:ResourceType>
1950 <rasd:Address>0</rasd:Address>
1951 <rasd:BusNumber>0</rasd:BusNumber>
1952 </Item> */
1953 if (uLoop == 1)
1954 {
1955 strDescription = "USB Controller";
1956 strCaption = "usb";
1957 type = ovf::ResourceType_USBController; // 23
1958 lAddress = 0; // this is what OVFTool writes
1959 lBusNumber = 0; // this is what OVFTool writes
1960 }
1961 break;
1962
1963 case VirtualSystemDescriptionType_SoundCard:
1964 /* <Item ovf:required="false">
1965 <rasd:Caption>sound</rasd:Caption>
1966 <rasd:Description>Sound Card</rasd:Description>
1967 <rasd:InstanceId>10</rasd:InstanceId>
1968 <rasd:ResourceType>35</rasd:ResourceType>
1969 <rasd:ResourceSubType>ensoniq1371</rasd:ResourceSubType>
1970 <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
1971 <rasd:AddressOnParent>3</rasd:AddressOnParent>
1972 </Item> */
1973 if (uLoop == 1)
1974 {
1975 strDescription = "Sound Card";
1976 strCaption = "sound";
1977 type = ovf::ResourceType_SoundCard; // 35
1978 strResourceSubType = desc.strOvf; // e.g. ensoniq1371
1979 lAutomaticAllocation = 0;
1980 lAddressOnParent = 3; // what gives? this is what OVFTool writes
1981 }
1982 break;
1983
1984 default: break; /* Shut up MSC. */
1985 }
1986
1987 if (type)
1988 {
1989 xml::ElementNode *pItem;
1990 xml::ElementNode *pItemHelper;
1991 RTCString itemElement;
1992 RTCString itemElementHelper;
1993
1994 if (enFormat == ovf::OVFVersion_2_0)
1995 {
1996 if(uLoop == 2)
1997 {
1998 if (desc.type == VirtualSystemDescriptionType_NetworkAdapter)
1999 {
2000 itemElement = "epasd:";
2001 pItem = pelmVirtualHardwareSection->createChild("EthernetPortItem");
2002 }
2003 else if (desc.type == VirtualSystemDescriptionType_CDROM ||
2004 desc.type == VirtualSystemDescriptionType_HardDiskImage)
2005 {
2006 itemElement = "sasd:";
2007 pItem = pelmVirtualHardwareSection->createChild("StorageItem");
2008 }
2009 else
2010 pItem = NULL;
2011 }
2012 else
2013 {
2014 itemElement = "rasd:";
2015 pItem = pelmVirtualHardwareSection->createChild("Item");
2016 }
2017 }
2018 else
2019 {
2020 itemElement = "rasd:";
2021 pItem = pelmVirtualHardwareSection->createChild("Item");
2022 }
2023
2024 // NOTE: DO NOT CHANGE THE ORDER of these items! The OVF standards prescribes that
2025 // the elements from the rasd: namespace must be sorted by letter, and VMware
2026 // actually requires this as well (see public bug #6612)
2027
2028 if (lAddress != -1)
2029 {
2030 //pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress));
2031 itemElementHelper = itemElement;
2032 pItemHelper = pItem->createChild(itemElementHelper.append("Address").c_str());
2033 pItemHelper->addContent(Utf8StrFmt("%d", lAddress));
2034 }
2035
2036 if (lAddressOnParent != -1)
2037 {
2038 //pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent));
2039 itemElementHelper = itemElement;
2040 pItemHelper = pItem->createChild(itemElementHelper.append("AddressOnParent").c_str());
2041 pItemHelper->addContent(Utf8StrFmt("%d", lAddressOnParent));
2042 }
2043
2044 if (!strAllocationUnits.isEmpty())
2045 {
2046 //pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits);
2047 itemElementHelper = itemElement;
2048 pItemHelper = pItem->createChild(itemElementHelper.append("AllocationUnits").c_str());
2049 pItemHelper->addContent(strAllocationUnits);
2050 }
2051
2052 if (lAutomaticAllocation != -1)
2053 {
2054 //pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" );
2055 itemElementHelper = itemElement;
2056 pItemHelper = pItem->createChild(itemElementHelper.append("AutomaticAllocation").c_str());
2057 pItemHelper->addContent((lAutomaticAllocation) ? "true" : "false" );
2058 }
2059
2060 if (lBusNumber != -1)
2061 {
2062 if (enFormat == ovf::OVFVersion_0_9)
2063 {
2064 // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool
2065 //pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber));
2066 itemElementHelper = itemElement;
2067 pItemHelper = pItem->createChild(itemElementHelper.append("BusNumber").c_str());
2068 pItemHelper->addContent(Utf8StrFmt("%d", lBusNumber));
2069 }
2070 }
2071
2072 if (!strCaption.isEmpty())
2073 {
2074 //pItem->createChild("rasd:Caption")->addContent(strCaption);
2075 itemElementHelper = itemElement;
2076 pItemHelper = pItem->createChild(itemElementHelper.append("Caption").c_str());
2077 pItemHelper->addContent(strCaption);
2078 }
2079
2080 if (!strConnection.isEmpty())
2081 {
2082 //pItem->createChild("rasd:Connection")->addContent(strConnection);
2083 itemElementHelper = itemElement;
2084 pItemHelper = pItem->createChild(itemElementHelper.append("Connection").c_str());
2085 pItemHelper->addContent(strConnection);
2086 }
2087
2088 if (!strDescription.isEmpty())
2089 {
2090 //pItem->createChild("rasd:Description")->addContent(strDescription);
2091 itemElementHelper = itemElement;
2092 pItemHelper = pItem->createChild(itemElementHelper.append("Description").c_str());
2093 pItemHelper->addContent(strDescription);
2094 }
2095
2096 if (!strCaption.isEmpty())
2097 {
2098 if (enFormat == ovf::OVFVersion_1_0)
2099 {
2100 //pItem->createChild("rasd:ElementName")->addContent(strCaption);
2101 itemElementHelper = itemElement;
2102 pItemHelper = pItem->createChild(itemElementHelper.append("ElementName").c_str());
2103 pItemHelper->addContent(strCaption);
2104 }
2105 }
2106
2107 if (!strHostResource.isEmpty())
2108 {
2109 //pItem->createChild("rasd:HostResource")->addContent(strHostResource);
2110 itemElementHelper = itemElement;
2111 pItemHelper = pItem->createChild(itemElementHelper.append("HostResource").c_str());
2112 pItemHelper->addContent(strHostResource);
2113 }
2114
2115 {
2116 // <rasd:InstanceID>1</rasd:InstanceID>
2117 itemElementHelper = itemElement;
2118 if (enFormat == ovf::OVFVersion_0_9)
2119 //pelmInstanceID = pItem->createChild("rasd:InstanceId");
2120 pItemHelper = pItem->createChild(itemElementHelper.append("InstanceId").c_str());
2121 else
2122 //pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed...
2123 pItemHelper = pItem->createChild(itemElementHelper.append("InstanceID").c_str());
2124
2125 pItemHelper->addContent(Utf8StrFmt("%d", ulInstanceID++));
2126 }
2127
2128 if (ulParent)
2129 {
2130 //pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent));
2131 itemElementHelper = itemElement;
2132 pItemHelper = pItem->createChild(itemElementHelper.append("Parent").c_str());
2133 pItemHelper->addContent(Utf8StrFmt("%d", ulParent));
2134 }
2135
2136 if (!strResourceSubType.isEmpty())
2137 {
2138 //pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType);
2139 itemElementHelper = itemElement;
2140 pItemHelper = pItem->createChild(itemElementHelper.append("ResourceSubType").c_str());
2141 pItemHelper->addContent(strResourceSubType);
2142 }
2143
2144 {
2145 // <rasd:ResourceType>3</rasd:ResourceType>
2146 //pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type));
2147 itemElementHelper = itemElement;
2148 pItemHelper = pItem->createChild(itemElementHelper.append("ResourceType").c_str());
2149 pItemHelper->addContent(Utf8StrFmt("%d", type));
2150 }
2151
2152 // <rasd:VirtualQuantity>1</rasd:VirtualQuantity>
2153 if (lVirtualQuantity != -1)
2154 {
2155 //pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity));
2156 itemElementHelper = itemElement;
2157 pItemHelper = pItem->createChild(itemElementHelper.append("VirtualQuantity").c_str());
2158 pItemHelper->addContent(Utf8StrFmt("%d", lVirtualQuantity));
2159 }
2160 }
2161 }
2162 } // for (size_t uLoop = 1; uLoop <= 2; ++uLoop)
2163
2164 // now that we're done with the official OVF <Item> tags under <VirtualSystem>, write out VirtualBox XML
2165 // under the vbox: namespace
2166 xml::ElementNode *pelmVBoxMachine = pelmVirtualSystem->createChild("vbox:Machine");
2167 // ovf:required="false" tells other OVF parsers that they can ignore this thing
2168 pelmVBoxMachine->setAttribute("ovf:required", "false");
2169 // ovf:Info element is required or VMware will bail out on the vbox:Machine element
2170 pelmVBoxMachine->createChild("ovf:Info")->addContent("Complete VirtualBox machine configuration in VirtualBox format");
2171
2172 // create an empty machine config
2173 // use the same settings version as the current VM settings file
2174 settings::MachineConfigFile *pConfig = new settings::MachineConfigFile(&vsdescThis->m->pMachine->i_getSettingsFileFull());
2175
2176 writeLock.release();
2177 try
2178 {
2179 AutoWriteLock machineLock(vsdescThis->m->pMachine COMMA_LOCKVAL_SRC_POS);
2180 // fill the machine config
2181 vsdescThis->m->pMachine->i_copyMachineDataToSettings(*pConfig);
2182 pConfig->machineUserData.strName = strVMName;
2183
2184 // Apply export tweaks to machine settings
2185 bool fStripAllMACs = m->optListExport.contains(ExportOptions_StripAllMACs);
2186 bool fStripAllNonNATMACs = m->optListExport.contains(ExportOptions_StripAllNonNATMACs);
2187 if (fStripAllMACs || fStripAllNonNATMACs)
2188 {
2189 for (settings::NetworkAdaptersList::iterator
2190 it = pConfig->hardwareMachine.llNetworkAdapters.begin();
2191 it != pConfig->hardwareMachine.llNetworkAdapters.end();
2192 ++it)
2193 {
2194 settings::NetworkAdapter &nic = *it;
2195 if (fStripAllMACs || (fStripAllNonNATMACs && nic.mode != NetworkAttachmentType_NAT))
2196 nic.strMACAddress.setNull();
2197 }
2198 }
2199
2200 // write the machine config to the vbox:Machine element
2201 pConfig->buildMachineXML(*pelmVBoxMachine,
2202 settings::MachineConfigFile::BuildMachineXML_WriteVBoxVersionAttribute
2203 /*| settings::MachineConfigFile::BuildMachineXML_SkipRemovableMedia*/
2204 | settings::MachineConfigFile::BuildMachineXML_SuppressSavedState,
2205 // but not BuildMachineXML_IncludeSnapshots nor BuildMachineXML_MediaRegistry
2206 pllElementsWithUuidAttributes);
2207 delete pConfig;
2208 }
2209 catch (...)
2210 {
2211 writeLock.acquire();
2212 delete pConfig;
2213 throw;
2214 }
2215 writeLock.acquire();
2216}
2217
2218/**
2219 * Actual worker code for writing out OVF/OVA to disk. This is called from Appliance::taskThreadWriteOVF()
2220 * and therefore runs on the OVF/OVA write worker thread.
2221 *
2222 * This runs in one context:
2223 *
2224 * 1) in a first worker thread; in that case, Appliance::Write() called Appliance::i_writeImpl();
2225 *
2226 * @param pTask
2227 * @return
2228 */
2229HRESULT Appliance::i_writeFS(TaskOVF *pTask)
2230{
2231 LogFlowFuncEnter();
2232 LogFlowFunc(("ENTER appliance %p\n", this));
2233
2234 AutoCaller autoCaller(this);
2235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2236
2237 HRESULT rc = S_OK;
2238
2239 // Lock the media tree early to make sure nobody else tries to make changes
2240 // to the tree. Also lock the IAppliance object for writing.
2241 AutoMultiWriteLock2 multiLock(&mVirtualBox->i_getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
2242 // Additional protect the IAppliance object, cause we leave the lock
2243 // when starting the disk export and we don't won't block other
2244 // callers on this lengthy operations.
2245 m->state = Data::ApplianceExporting;
2246
2247 if (pTask->locInfo.strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
2248 rc = i_writeFSOVF(pTask, multiLock);
2249 else
2250 rc = i_writeFSOVA(pTask, multiLock);
2251
2252 // reset the state so others can call methods again
2253 m->state = Data::ApplianceIdle;
2254
2255 LogFlowFunc(("rc=%Rhrc\n", rc));
2256 LogFlowFuncLeave();
2257 return rc;
2258}
2259
2260HRESULT Appliance::i_writeFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock)
2261{
2262 LogFlowFuncEnter();
2263
2264 /*
2265 * Create write-to-dir file system stream for the target directory.
2266 * This unifies the disk access with the TAR based OVA variant.
2267 */
2268 HRESULT hrc;
2269 int vrc;
2270 RTVFSFSSTREAM hVfsFss2Dir = NIL_RTVFSFSSTREAM;
2271 try
2272 {
2273 Utf8Str strTargetDir(pTask->locInfo.strPath);
2274 strTargetDir.stripFilename();
2275 vrc = RTVfsFsStrmToNormalDir(strTargetDir.c_str(), 0 /*fFlags*/, &hVfsFss2Dir);
2276 if (RT_SUCCESS(vrc))
2277 hrc = S_OK;
2278 else
2279 hrc = setErrorVrc(vrc, tr("Failed to open directory '%s' (%Rrc)"), strTargetDir.c_str(), vrc);
2280 }
2281 catch (std::bad_alloc &)
2282 {
2283 hrc = E_OUTOFMEMORY;
2284 }
2285 if (SUCCEEDED(hrc))
2286 {
2287 /*
2288 * Join i_writeFSOVA. On failure, delete (undo) anything we might
2289 * have written to the disk before failing.
2290 */
2291 hrc = i_writeFSImpl(pTask, writeLock, hVfsFss2Dir);
2292 if (FAILED(hrc))
2293 RTVfsFsStrmToDirUndo(hVfsFss2Dir);
2294 RTVfsFsStrmRelease(hVfsFss2Dir);
2295 }
2296
2297 LogFlowFuncLeave();
2298 return hrc;
2299}
2300
2301HRESULT Appliance::i_writeFSOVA(TaskOVF *pTask, AutoWriteLockBase &writeLock)
2302{
2303 LogFlowFuncEnter();
2304
2305 /*
2306 * Open the output file and attach a TAR creator to it.
2307 * The OVF 1.1.0 spec specifies the TAR format to be compatible with USTAR
2308 * according to POSIX 1003.1-2008. We use the 1988 spec here as it's the
2309 * only variant we currently implement.
2310 */
2311 HRESULT hrc;
2312 RTVFSIOSTREAM hVfsIosTar;
2313 int vrc = RTVfsIoStrmOpenNormal(pTask->locInfo.strPath.c_str(),
2314 RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
2315 &hVfsIosTar);
2316 if (RT_SUCCESS(vrc))
2317 {
2318 RTVFSFSSTREAM hVfsFssTar;
2319 vrc = RTZipTarFsStreamToIoStream(hVfsIosTar, RTZIPTARFORMAT_USTAR, 0 /*fFlags*/, &hVfsFssTar);
2320 RTVfsIoStrmRelease(hVfsIosTar);
2321 if (RT_SUCCESS(vrc))
2322 {
2323 RTZipTarFsStreamSetFileMode(hVfsFssTar, 0660, 0440);
2324 RTZipTarFsStreamSetOwner(hVfsFssTar, VBOX_VERSION_MAJOR,
2325 pTask->enFormat == ovf::OVFVersion_0_9 ? "vboxovf09"
2326 : pTask->enFormat == ovf::OVFVersion_1_0 ? "vboxovf10"
2327 : pTask->enFormat == ovf::OVFVersion_2_0 ? "vboxovf20"
2328 : "vboxovf");
2329 RTZipTarFsStreamSetGroup(hVfsFssTar, VBOX_VERSION_MINOR,
2330 "vbox_v" RT_XSTR(VBOX_VERSION_MAJOR) "." RT_XSTR(VBOX_VERSION_MINOR) "."
2331 RT_XSTR(VBOX_VERSION_BUILD) "r" RT_XSTR(VBOX_SVN_REV));
2332
2333 hrc = i_writeFSImpl(pTask, writeLock, hVfsFssTar);
2334 RTVfsFsStrmRelease(hVfsFssTar);
2335 }
2336 else
2337 hrc = setErrorVrc(vrc, tr("Failed create TAR creator for '%s' (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
2338
2339 /* Delete the OVA on failure. */
2340 if (FAILED(hrc))
2341 RTFileDelete(pTask->locInfo.strPath.c_str());
2342 }
2343 else
2344 hrc = setErrorVrc(vrc, tr("Failed to open '%s' for writing (%Rrc)"), pTask->locInfo.strPath.c_str(), vrc);
2345
2346 LogFlowFuncLeave();
2347 return hrc;
2348}
2349
2350/**
2351 * Upload the image to the OCI Storage service, next import the
2352 * uploaded image into internal OCI image format and launch an
2353 * instance with this image in the OCI Compute service.
2354 */
2355HRESULT Appliance::i_writeFSOCI(TaskOCI *pTask)
2356{
2357 LogRel(("Appliance::i_writeFSOCI\n"));
2358
2359 RT_NOREF(pTask); // XXX
2360 LogFlowFuncEnter();
2361
2362 HRESULT hrc = S_OK;
2363 ComPtr<ICloudProviderManager> cpm;
2364 hrc = mVirtualBox->COMGETTER(CloudProviderManager)(cpm.asOutParam());
2365 Utf8Str strProviderName("OCI");
2366 ComPtr<ICloudProvider> ociProvider;
2367 hrc = cpm->GetProviderByShortName(Bstr(strProviderName.c_str()).raw(), ociProvider.asOutParam());
2368 ComPtr<ICloudProfile> ociProfile;
2369 hrc = ociProvider->GetProfileByName(Bstr(m->m_OciExportData.strProfileName.c_str()).raw(), ociProfile.asOutParam());
2370 ComObjPtr<ICloudClient> cloudClient;
2371 hrc = ociProfile->CreateCloudClient(cloudClient.asOutParam());
2372
2373#ifndef VBOX_WITH_CLOUD_PROVIDERS_NO_COMMANDS
2374
2375 int vrc = VINF_SUCCESS;
2376 //fills by values from m->m_OciExportData
2377 //mostly all names(keys) come from official OCI API documentation (see LaunchInstance description)
2378 SafeArray <BSTR> paramNames;
2379 SafeArray <BSTR> paramValues;
2380
2381 Bstr("displayName").detachTo(paramNames.appendedRaw());
2382 Bstr(m->m_OciExportData.strDisplayMachineName).detachTo(paramValues.appendedRaw());
2383 Bstr("objectName").detachTo(paramNames.appendedRaw());
2384 Bstr(m->m_OciExportData.strBootImageName).detachTo(paramValues.appendedRaw());
2385 Bstr("vcnId").detachTo(paramNames.appendedRaw());
2386 Bstr(m->m_OciExportData.strVCN).detachTo(paramValues.appendedRaw());
2387 Bstr("bucketName").detachTo(paramNames.appendedRaw());
2388 Bstr(m->m_OciExportData.strBucketId).detachTo(paramValues.appendedRaw());
2389 Bstr("bootVolumeSizeInGBs").detachTo(paramNames.appendedRaw());
2390 Bstr(m->m_OciExportData.strBootDiskSize).detachTo(paramValues.appendedRaw());
2391 Bstr("availabilityDomain").detachTo(paramNames.appendedRaw());
2392 Bstr(m->m_OciExportData.strDomainName).detachTo(paramValues.appendedRaw());
2393 Bstr("shape").detachTo(paramNames.appendedRaw());
2394 Bstr(m->m_OciExportData.strInstanceShapeId).detachTo(paramValues.appendedRaw());
2395 Bstr("profileName").detachTo(paramNames.appendedRaw());
2396 Bstr(m->m_OciExportData.strProfileName).detachTo(paramValues.appendedRaw());
2397 Bstr("assignPublicIp").detachTo(paramNames.appendedRaw());
2398 Utf8Str fIP = (m->m_OciExportData.fPublicIP == true) ? "true" : "false";
2399 Bstr(fIP.c_str()).detachTo(paramValues.appendedRaw());
2400
2401 com::SafeArray<CloudCommand_T> commandList;
2402
2403 if (SUCCEEDED(hrc))
2404 {
2405 try
2406 {
2407 hrc = cloudClient->GetCommandsForOperation(CloudOperation_exportVM, false,
2408 ComSafeArrayAsOutParam(commandList));
2409 if (SUCCEEDED(hrc))
2410 {
2411 SafeArray <BSTR> commandIdList;
2412
2413 for (ULONG i = 0; i < commandList.size(); i++)
2414 {
2415 CloudCommand_T cmd = commandList[i];
2416 Utf8Str cond;
2417 switch(cmd)
2418 {
2419 case CloudCommand_getImage:
2420 case CloudCommand_getSubnet:
2421 cond.assign("AVAILABLE");
2422 break;
2423 case CloudCommand_getInstance:
2424 cond.assign("RUNNING");
2425 break;
2426 default:
2427 break;
2428 }
2429
2430 Bstr bStrId;
2431 hrc = cloudClient->CreateCommand(cmd, (cond.isEmpty()) ? NULL : Bstr(cond.c_str()).raw(), bStrId.asOutParam());
2432
2433 if (SUCCEEDED(hrc))
2434 {
2435 bStrId.detachTo(commandIdList.appendedRaw());
2436 }
2437 }
2438
2439 if (SUCCEEDED(hrc))
2440 vrc = cloudClient->RunSeveralCommands(ComSafeArrayAsInParam(commandIdList),
2441 ComSafeArrayAsInParam(paramNames),
2442 ComSafeArrayAsInParam(paramValues),
2443 pTask->pProgress);
2444 if (RT_FAILURE(vrc))
2445 hrc = E_FAIL;
2446 }
2447 }
2448 catch (HRESULT arc)
2449 {
2450 hrc = arc;
2451 }
2452 catch (...)
2453 {
2454 LogRel(("Appliance::i_writeFSOCI(): get caught unknown exception\n"));
2455 }
2456
2457 cloudClient.setNull();
2458 }
2459#else
2460 LogRel(("Appliance::i_writeFSOCI(): #ifdef VBOX_WITH_CLOUD_PROVIDERS_NO_COMMANDS section \n"));
2461 if (SUCCEEDED(hrc))
2462 {
2463 LogRel(("Appliance::i_writeFSOCI(): calling OCICloudClient::exportVM\n"));
2464
2465 /// @todo that's to be moved to ExpTack, but we need to have that method
2466 /// exposed in .xidl
2467 if (m->virtualSystemDescriptions.size() == 1) {
2468 ComPtr<IVirtualBox> VBox(mVirtualBox);
2469
2470 pTask->pProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
2471 Bstr("Exporting VM to OCI...").raw(),
2472 TRUE /* aCancelable */,
2473 5, // ULONG cOperations,
2474 1000, // ULONG ulTotalOperationsWeight,
2475 Bstr("Exporting VM to OCI...").raw(), // aFirstOperationDescription
2476 10); // ULONG ulFirstOperationWeight,
2477
2478 hrc = cloudClient->ExportVM(m->virtualSystemDescriptions.front(), pTask->pProgress, VBox);
2479 } else {
2480 /// @todo Fail here with user notification. We do export 1 VM only
2481 }
2482 }
2483#endif
2484
2485 LogFlowFuncLeave();
2486 return hrc;
2487}
2488/**
2489 * Writes the Oracle Public Cloud appliance.
2490 *
2491 * It expect raw disk images inside a gzipped tarball. We enable sparse files
2492 * to save diskspace on the target host system.
2493 */
2494HRESULT Appliance::i_writeFSOPC(TaskOPC *pTask)
2495{
2496 LogFlowFuncEnter();
2497 HRESULT hrc = S_OK;
2498
2499 // Lock the media tree early to make sure nobody else tries to make changes
2500 // to the tree. Also lock the IAppliance object for writing.
2501 AutoMultiWriteLock2 multiLock(&mVirtualBox->i_getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
2502 // Additional protect the IAppliance object, cause we leave the lock
2503 // when starting the disk export and we don't won't block other
2504 // callers on this lengthy operations.
2505 m->state = Data::ApplianceExporting;
2506
2507 /*
2508 * We're duplicating parts of i_writeFSImpl here because that's simpler
2509 * and creates less spaghetti code.
2510 */
2511 std::list<Utf8Str> lstTarballs;
2512
2513 /*
2514 * Use i_buildXML to build a stack of disk images. We don't care about the XML doc here.
2515 */
2516 XMLStack stack;
2517 {
2518 xml::Document doc;
2519 i_buildXML(multiLock, doc, stack, pTask->locInfo.strPath, ovf::OVFVersion_2_0);
2520 }
2521
2522 /*
2523 * Process the disk images.
2524 */
2525 unsigned cTarballs = 0;
2526 for (list<Utf8Str>::const_iterator it = stack.mapDiskSequence.begin();
2527 it != stack.mapDiskSequence.end();
2528 ++it)
2529 {
2530 const Utf8Str &strDiskID = *it;
2531 const VirtualSystemDescriptionEntry *pDiskEntry = stack.mapDisks[strDiskID];
2532 const Utf8Str &strSrcFilePath = pDiskEntry->strVBoxCurrent; // where the VBox image is
2533
2534 /*
2535 * Some skipping.
2536 */
2537 if (pDiskEntry->skipIt)
2538 continue;
2539
2540 /* Skip empty media (DVD-ROM, floppy). */
2541 if (strSrcFilePath.isEmpty())
2542 continue;
2543
2544 /* Only deal with harddisk and DVD-ROMs, skip any floppies for now. */
2545 if ( pDiskEntry->type != VirtualSystemDescriptionType_HardDiskImage
2546 && pDiskEntry->type != VirtualSystemDescriptionType_CDROM)
2547 continue;
2548
2549 /*
2550 * Locate the Medium object for this entry (by location/path).
2551 */
2552 Log(("Finding source disk \"%s\"\n", strSrcFilePath.c_str()));
2553 ComObjPtr<Medium> ptrSourceDisk;
2554 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)
2555 hrc = mVirtualBox->i_findHardDiskByLocation(strSrcFilePath, true /*aSetError*/, &ptrSourceDisk);
2556 else
2557 hrc = mVirtualBox->i_findDVDOrFloppyImage(DeviceType_DVD, NULL /*aId*/, strSrcFilePath,
2558 true /*aSetError*/, &ptrSourceDisk);
2559 if (FAILED(hrc))
2560 break;
2561 if (strSrcFilePath.isEmpty())
2562 continue;
2563
2564 /*
2565 * Figure out the names.
2566 */
2567
2568 /* The name inside the tarball. Replace the suffix of harddisk images with ".img". */
2569 Utf8Str strInsideName = pDiskEntry->strOvf;
2570 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)
2571 strInsideName.stripSuffix().append(".img");
2572
2573 /* The first tarball we create uses the specified name. Subsequent
2574 takes the name from the disk entry or something. */
2575 Utf8Str strTarballPath = pTask->locInfo.strPath;
2576 if (cTarballs > 0)
2577 {
2578 strTarballPath.stripFilename().append(RTPATH_SLASH_STR).append(pDiskEntry->strOvf);
2579 const char *pszExt = RTPathSuffix(pDiskEntry->strOvf.c_str());
2580 if (pszExt && pszExt[0] == '.' && pszExt[1] != '\0')
2581 {
2582 strTarballPath.stripSuffix();
2583 if (pDiskEntry->type != VirtualSystemDescriptionType_HardDiskImage)
2584 strTarballPath.append("_").append(&pszExt[1]);
2585 }
2586 strTarballPath.append(".tar.gz");
2587 }
2588 cTarballs++;
2589
2590 /*
2591 * Create the tar output stream.
2592 */
2593 RTVFSIOSTREAM hVfsIosFile;
2594 int vrc = RTVfsIoStrmOpenNormal(strTarballPath.c_str(),
2595 RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
2596 &hVfsIosFile);
2597 if (RT_SUCCESS(vrc))
2598 {
2599 RTVFSIOSTREAM hVfsIosGzip = NIL_RTVFSIOSTREAM;
2600 vrc = RTZipGzipCompressIoStream(hVfsIosFile, 0 /*fFlags*/, 6 /*uLevel*/, &hVfsIosGzip);
2601 RTVfsIoStrmRelease(hVfsIosFile);
2602
2603 /** @todo insert I/O thread here between gzip and the tar creator. Needs
2604 * implementing. */
2605
2606 RTVFSFSSTREAM hVfsFssTar = NIL_RTVFSFSSTREAM;
2607 if (RT_SUCCESS(vrc))
2608 vrc = RTZipTarFsStreamToIoStream(hVfsIosGzip, RTZIPTARFORMAT_GNU, RTZIPTAR_C_SPARSE, &hVfsFssTar);
2609 RTVfsIoStrmRelease(hVfsIosGzip);
2610 if (RT_SUCCESS(vrc))
2611 {
2612 RTZipTarFsStreamSetFileMode(hVfsFssTar, 0660, 0440);
2613 RTZipTarFsStreamSetOwner(hVfsFssTar, VBOX_VERSION_MAJOR, "vboxopc10");
2614 RTZipTarFsStreamSetGroup(hVfsFssTar, VBOX_VERSION_MINOR,
2615 "vbox_v" RT_XSTR(VBOX_VERSION_MAJOR) "." RT_XSTR(VBOX_VERSION_MINOR) "."
2616 RT_XSTR(VBOX_VERSION_BUILD) "r" RT_XSTR(VBOX_SVN_REV));
2617
2618 /*
2619 * Let the Medium code do the heavy work.
2620 *
2621 * The exporting requests a lock on the media tree. So temporarily
2622 * leave the appliance lock.
2623 */
2624 multiLock.release();
2625
2626 pTask->pProgress->SetNextOperation(BstrFmt(tr("Exporting to disk image '%Rbn'"), strTarballPath.c_str()).raw(),
2627 pDiskEntry->ulSizeMB); // operation's weight, as set up
2628 // with the IProgress originally
2629 hrc = ptrSourceDisk->i_addRawToFss(strInsideName.c_str(), m->m_pSecretKeyStore, hVfsFssTar,
2630 pTask->pProgress, true /*fSparse*/);
2631
2632 multiLock.acquire();
2633 if (SUCCEEDED(hrc))
2634 {
2635 /*
2636 * Complete and close the tarball.
2637 */
2638 vrc = RTVfsFsStrmEnd(hVfsFssTar);
2639 RTVfsFsStrmRelease(hVfsFssTar);
2640 hVfsFssTar = NIL_RTVFSFSSTREAM;
2641 if (RT_SUCCESS(vrc))
2642 {
2643 /* Remember the tarball name for cleanup. */
2644 try
2645 {
2646 lstTarballs.push_back(strTarballPath.c_str());
2647 strTarballPath.setNull();
2648 }
2649 catch (std::bad_alloc &)
2650 { hrc = E_OUTOFMEMORY; }
2651 }
2652 else
2653 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
2654 tr("Error completing TAR file '%s' (%Rrc)"), strTarballPath.c_str(), vrc);
2655 }
2656 }
2657 else
2658 hrc = setErrorVrc(vrc, tr("Failed to TAR creator instance for '%s' (%Rrc)"), strTarballPath.c_str(), vrc);
2659
2660 if (FAILED(hrc) && strTarballPath.isNotEmpty())
2661 RTFileDelete(strTarballPath.c_str());
2662 }
2663 else
2664 hrc = setErrorVrc(vrc, tr("Failed to create '%s' (%Rrc)"), strTarballPath.c_str(), vrc);
2665 if (FAILED(hrc))
2666 break;
2667 }
2668
2669 /*
2670 * Delete output files on failure.
2671 */
2672 if (FAILED(hrc))
2673 for (list<Utf8Str>::const_iterator it = lstTarballs.begin(); it != lstTarballs.end(); ++it)
2674 RTFileDelete(it->c_str());
2675
2676 // reset the state so others can call methods again
2677 m->state = Data::ApplianceIdle;
2678
2679 LogFlowFuncLeave();
2680 return hrc;
2681
2682}
2683
2684HRESULT Appliance::i_writeFSImpl(TaskOVF *pTask, AutoWriteLockBase &writeLock, RTVFSFSSTREAM hVfsFssDst)
2685{
2686 LogFlowFuncEnter();
2687
2688 HRESULT rc = S_OK;
2689 int vrc;
2690 try
2691 {
2692 // the XML stack contains two maps for disks and networks, which allows us to
2693 // a) have a list of unique disk names (to make sure the same disk name is only added once)
2694 // and b) keep a list of all networks
2695 XMLStack stack;
2696 // Scope this to free the memory as soon as this is finished
2697 {
2698 /* Construct the OVF name. */
2699 Utf8Str strOvfFile(pTask->locInfo.strPath);
2700 strOvfFile.stripPath().stripSuffix().append(".ovf");
2701
2702 /* Render a valid ovf document into a memory buffer. The unknown
2703 version upgrade relates to the OPC hack up in Appliance::write(). */
2704 xml::Document doc;
2705 i_buildXML(writeLock, doc, stack, pTask->locInfo.strPath,
2706 pTask->enFormat != ovf::OVFVersion_unknown ? pTask->enFormat : ovf::OVFVersion_2_0);
2707
2708 void *pvBuf = NULL;
2709 size_t cbSize = 0;
2710 xml::XmlMemWriter writer;
2711 writer.write(doc, &pvBuf, &cbSize);
2712 if (RT_UNLIKELY(!pvBuf))
2713 throw setError(VBOX_E_FILE_ERROR, tr("Could not create OVF file '%s'"), strOvfFile.c_str());
2714
2715 /* Write the ovf file to "disk". */
2716 rc = i_writeBufferToFile(hVfsFssDst, strOvfFile.c_str(), pvBuf, cbSize);
2717 if (FAILED(rc))
2718 throw rc;
2719 }
2720
2721 // We need a proper format description
2722 ComObjPtr<MediumFormat> formatTemp;
2723
2724 ComObjPtr<MediumFormat> format;
2725 // Scope for the AutoReadLock
2726 {
2727 SystemProperties *pSysProps = mVirtualBox->i_getSystemProperties();
2728 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
2729 // We are always exporting to VMDK stream optimized for now
2730 formatTemp = pSysProps->i_mediumFormatFromExtension("iso");
2731
2732 format = pSysProps->i_mediumFormat("VMDK");
2733 if (format.isNull())
2734 throw setError(VBOX_E_NOT_SUPPORTED,
2735 tr("Invalid medium storage format"));
2736 }
2737
2738 // Finally, write out the disks!
2739 //use the list stack.mapDiskSequence where the disks were put as the "VirtualSystem"s had been placed
2740 //in the OVF description file. I.e. we have one "VirtualSystem" in the OVF file, we extract all disks
2741 //attached to it. And these disks are stored in the stack.mapDiskSequence. Next we shift to the next
2742 //"VirtualSystem" and repeat the operation.
2743 //And here we go through the list and extract all disks in the same sequence
2744 for (list<Utf8Str>::const_iterator
2745 it = stack.mapDiskSequence.begin();
2746 it != stack.mapDiskSequence.end();
2747 ++it)
2748 {
2749 const Utf8Str &strDiskID = *it;
2750 const VirtualSystemDescriptionEntry *pDiskEntry = stack.mapDisks[strDiskID];
2751
2752 // source path: where the VBox image is
2753 const Utf8Str &strSrcFilePath = pDiskEntry->strVBoxCurrent;
2754
2755 //skip empty Medium. In common, It's may be empty CD/DVD
2756 if (strSrcFilePath.isEmpty() ||
2757 pDiskEntry->skipIt == true)
2758 continue;
2759
2760 // Do NOT check here whether the file exists. findHardDisk will
2761 // figure that out, and filesystem-based tests are simply wrong
2762 // in the general case (think of iSCSI).
2763
2764 // clone the disk:
2765 ComObjPtr<Medium> pSourceDisk;
2766
2767 Log(("Finding source disk \"%s\"\n", strSrcFilePath.c_str()));
2768
2769 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)
2770 {
2771 rc = mVirtualBox->i_findHardDiskByLocation(strSrcFilePath, true, &pSourceDisk);
2772 if (FAILED(rc)) throw rc;
2773 }
2774 else//may be CD or DVD
2775 {
2776 rc = mVirtualBox->i_findDVDOrFloppyImage(DeviceType_DVD,
2777 NULL,
2778 strSrcFilePath,
2779 true,
2780 &pSourceDisk);
2781 if (FAILED(rc)) throw rc;
2782 }
2783
2784 Bstr uuidSource;
2785 rc = pSourceDisk->COMGETTER(Id)(uuidSource.asOutParam());
2786 if (FAILED(rc)) throw rc;
2787 Guid guidSource(uuidSource);
2788
2789 // output filename
2790 const Utf8Str &strTargetFileNameOnly = pDiskEntry->strOvf;
2791
2792 // target path needs to be composed from where the output OVF is
2793 const Utf8Str &strTargetFilePath = strTargetFileNameOnly;
2794
2795 // The exporting requests a lock on the media tree. So leave our lock temporary.
2796 writeLock.release();
2797 try
2798 {
2799 // advance to the next operation
2800 pTask->pProgress->SetNextOperation(BstrFmt(tr("Exporting to disk image '%s'"),
2801 RTPathFilename(strTargetFilePath.c_str())).raw(),
2802 pDiskEntry->ulSizeMB); // operation's weight, as set up
2803 // with the IProgress originally
2804
2805 // create a flat copy of the source disk image
2806 if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)
2807 {
2808 /*
2809 * Export a disk image.
2810 */
2811 /* For compressed VMDK fun, we let i_exportFile produce the image bytes. */
2812 RTVFSIOSTREAM hVfsIosDst;
2813 vrc = RTVfsFsStrmPushFile(hVfsFssDst, strTargetFilePath.c_str(), UINT64_MAX,
2814 NULL /*paObjInfo*/, 0 /*cObjInfo*/, RTVFSFSSTRM_PUSH_F_STREAM, &hVfsIosDst);
2815 if (RT_FAILURE(vrc))
2816 throw setErrorVrc(vrc, tr("RTVfsFsStrmPushFile failed for '%s' (%Rrc)"), strTargetFilePath.c_str(), vrc);
2817 hVfsIosDst = i_manifestSetupDigestCalculationForGivenIoStream(hVfsIosDst, strTargetFilePath.c_str(),
2818 false /*fRead*/);
2819 if (hVfsIosDst == NIL_RTVFSIOSTREAM)
2820 throw setError(E_FAIL, "i_manifestSetupDigestCalculationForGivenIoStream(%s)", strTargetFilePath.c_str());
2821
2822 rc = pSourceDisk->i_exportFile(strTargetFilePath.c_str(),
2823 format,
2824 MediumVariant_VmdkStreamOptimized,
2825 m->m_pSecretKeyStore,
2826 hVfsIosDst,
2827 pTask->pProgress);
2828 RTVfsIoStrmRelease(hVfsIosDst);
2829 }
2830 else
2831 {
2832 /*
2833 * Copy CD/DVD/floppy image.
2834 */
2835 Assert(pDiskEntry->type == VirtualSystemDescriptionType_CDROM);
2836 rc = pSourceDisk->i_addRawToFss(strTargetFilePath.c_str(), m->m_pSecretKeyStore, hVfsFssDst,
2837 pTask->pProgress, false /*fSparse*/);
2838 }
2839 if (FAILED(rc)) throw rc;
2840 }
2841 catch (HRESULT rc3)
2842 {
2843 writeLock.acquire();
2844 /// @todo file deletion on error? If not, we can remove that whole try/catch block.
2845 throw rc3;
2846 }
2847 // Finished, lock again (so nobody mess around with the medium tree
2848 // in the meantime)
2849 writeLock.acquire();
2850 }
2851
2852 if (m->fManifest)
2853 {
2854 // Create & write the manifest file
2855 Utf8Str strMfFilePath = Utf8Str(pTask->locInfo.strPath).stripSuffix().append(".mf");
2856 Utf8Str strMfFileName = Utf8Str(strMfFilePath).stripPath();
2857 pTask->pProgress->SetNextOperation(BstrFmt(tr("Creating manifest file '%s'"), strMfFileName.c_str()).raw(),
2858 m->ulWeightForManifestOperation); // operation's weight, as set up
2859 // with the IProgress originally);
2860 /* Create a memory I/O stream and write the manifest to it. */
2861 RTVFSIOSTREAM hVfsIosManifest;
2862 vrc = RTVfsMemIoStrmCreate(NIL_RTVFSIOSTREAM, _1K, &hVfsIosManifest);
2863 if (RT_FAILURE(vrc))
2864 throw setErrorVrc(vrc, tr("RTVfsMemIoStrmCreate failed (%Rrc)"), vrc);
2865 if (m->hOurManifest != NIL_RTMANIFEST) /* In case it's empty. */
2866 vrc = RTManifestWriteStandard(m->hOurManifest, hVfsIosManifest);
2867 if (RT_SUCCESS(vrc))
2868 {
2869 /* Rewind the stream and add it to the output. */
2870 size_t cbIgnored;
2871 vrc = RTVfsIoStrmReadAt(hVfsIosManifest, 0 /*offset*/, &cbIgnored, 0, true /*fBlocking*/, &cbIgnored);
2872 if (RT_SUCCESS(vrc))
2873 {
2874 RTVFSOBJ hVfsObjManifest = RTVfsObjFromIoStream(hVfsIosManifest);
2875 vrc = RTVfsFsStrmAdd(hVfsFssDst, strMfFileName.c_str(), hVfsObjManifest, 0 /*fFlags*/);
2876 if (RT_SUCCESS(vrc))
2877 rc = S_OK;
2878 else
2879 rc = setErrorVrc(vrc, tr("RTVfsFsStrmAdd failed for the manifest (%Rrc)"), vrc);
2880 }
2881 else
2882 rc = setErrorVrc(vrc, tr("RTManifestWriteStandard failed (%Rrc)"), vrc);
2883 }
2884 else
2885 rc = setErrorVrc(vrc, tr("RTManifestWriteStandard failed (%Rrc)"), vrc);
2886 RTVfsIoStrmRelease(hVfsIosManifest);
2887 if (FAILED(rc))
2888 throw rc;
2889 }
2890 }
2891 catch (RTCError &x) // includes all XML exceptions
2892 {
2893 rc = setError(VBOX_E_FILE_ERROR,
2894 x.what());
2895 }
2896 catch (HRESULT aRC)
2897 {
2898 rc = aRC;
2899 }
2900
2901 LogFlowFunc(("rc=%Rhrc\n", rc));
2902 LogFlowFuncLeave();
2903
2904 return rc;
2905}
2906
2907
2908/**
2909 * Writes a memory buffer to a file in the output file system stream.
2910 *
2911 * @returns COM status code.
2912 * @param hVfsFssDst The file system stream to add the file to.
2913 * @param pszFilename The file name (w/ path if desired).
2914 * @param pvContent Pointer to buffer containing the file content.
2915 * @param cbContent Size of the content.
2916 */
2917HRESULT Appliance::i_writeBufferToFile(RTVFSFSSTREAM hVfsFssDst, const char *pszFilename, const void *pvContent, size_t cbContent)
2918{
2919 /*
2920 * Create a VFS file around the memory, converting it to a base VFS object handle.
2921 */
2922 HRESULT hrc;
2923 RTVFSIOSTREAM hVfsIosSrc;
2924 int vrc = RTVfsIoStrmFromBuffer(RTFILE_O_READ, pvContent, cbContent, &hVfsIosSrc);
2925 if (RT_SUCCESS(vrc))
2926 {
2927 hVfsIosSrc = i_manifestSetupDigestCalculationForGivenIoStream(hVfsIosSrc, pszFilename);
2928 AssertReturn(hVfsIosSrc != NIL_RTVFSIOSTREAM,
2929 setErrorVrc(vrc, "i_manifestSetupDigestCalculationForGivenIoStream"));
2930
2931 RTVFSOBJ hVfsObj = RTVfsObjFromIoStream(hVfsIosSrc);
2932 RTVfsIoStrmRelease(hVfsIosSrc);
2933 AssertReturn(hVfsObj != NIL_RTVFSOBJ, E_FAIL);
2934
2935 /*
2936 * Add it to the stream.
2937 */
2938 vrc = RTVfsFsStrmAdd(hVfsFssDst, pszFilename, hVfsObj, 0);
2939 RTVfsObjRelease(hVfsObj);
2940 if (RT_SUCCESS(vrc))
2941 hrc = S_OK;
2942 else
2943 hrc = setErrorVrc(vrc, tr("RTVfsFsStrmAdd failed for '%s' (%Rrc)"), pszFilename, vrc);
2944 }
2945 else
2946 hrc = setErrorVrc(vrc, "RTVfsIoStrmFromBuffer");
2947 return hrc;
2948}
2949
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