VirtualBox

source: vbox/trunk/src/VBox/Main/ApplianceImplImport.cpp@ 28531

Last change on this file since 28531 was 28531, checked in by vboxsync, 15 years ago

Main/OVF: fix import of IDE controllers (export currently broken)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 93.7 KB
Line 
1/* $Id: ApplianceImplImport.cpp 28531 2010-04-20 16:22:41Z vboxsync $ */
2/** @file
3 *
4 * IAppliance and IVirtualSystem COM class implementations.
5 */
6
7/*
8 * Copyright (C) 2008-2010 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23#include <iprt/path.h>
24#include <iprt/dir.h>
25#include <iprt/file.h>
26#include <iprt/s3.h>
27#include <iprt/sha.h>
28#include <iprt/manifest.h>
29
30#include <VBox/com/array.h>
31
32#include "ApplianceImpl.h"
33#include "VirtualBoxImpl.h"
34#include "GuestOSTypeImpl.h"
35#include "ProgressImpl.h"
36#include "MachineImpl.h"
37
38#include "AutoCaller.h"
39#include "Logging.h"
40
41#include "ApplianceImplPrivate.h"
42
43#include <VBox/param.h>
44#include <VBox/version.h>
45#include <VBox/settings.h>
46
47using namespace std;
48
49////////////////////////////////////////////////////////////////////////////////
50//
51// IAppliance public methods
52//
53////////////////////////////////////////////////////////////////////////////////
54
55/**
56 * Public method implementation.
57 * @param path
58 * @return
59 */
60STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress)
61{
62 if (!path) return E_POINTER;
63 CheckComArgOutPointerValid(aProgress);
64
65 AutoCaller autoCaller(this);
66 if (FAILED(autoCaller.rc())) return autoCaller.rc();
67
68 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
69
70 if (!isApplianceIdle())
71 return E_ACCESSDENIED;
72
73 if (m->pReader)
74 {
75 delete m->pReader;
76 m->pReader = NULL;
77 }
78
79 // see if we can handle this file; for now we insist it has an ".ovf" extension
80 Utf8Str strPath (path);
81 if (!strPath.endsWith(".ovf", Utf8Str::CaseInsensitive))
82 return setError(VBOX_E_FILE_ERROR,
83 tr("Appliance file must have .ovf extension"));
84
85 ComObjPtr<Progress> progress;
86 HRESULT rc = S_OK;
87 try
88 {
89 /* Parse all necessary info out of the URI */
90 parseURI(strPath, m->locInfo);
91 rc = readImpl(m->locInfo, progress);
92 }
93 catch (HRESULT aRC)
94 {
95 rc = aRC;
96 }
97
98 if (SUCCEEDED(rc))
99 /* Return progress to the caller */
100 progress.queryInterfaceTo(aProgress);
101
102 return S_OK;
103}
104
105/**
106 * Public method implementation.
107 * @return
108 */
109STDMETHODIMP Appliance::Interpret()
110{
111 // @todo:
112 // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk))
113 // - Appropriate handle errors like not supported file formats
114 AutoCaller autoCaller(this);
115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
116
117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
118
119 if (!isApplianceIdle())
120 return E_ACCESSDENIED;
121
122 HRESULT rc = S_OK;
123
124 /* Clear any previous virtual system descriptions */
125 m->virtualSystemDescriptions.clear();
126
127 Utf8Str strDefaultHardDiskFolder;
128 rc = getDefaultHardDiskFolder(strDefaultHardDiskFolder);
129 if (FAILED(rc)) return rc;
130
131 if (!m->pReader)
132 return setError(E_FAIL,
133 tr("Cannot interpret appliance without reading it first (call read() before interpret())"));
134
135 // Change the appliance state so we can safely leave the lock while doing time-consuming
136 // disk imports; also the below method calls do all kinds of locking which conflicts with
137 // the appliance object lock
138 m->state = Data::ApplianceImporting;
139 alock.release();
140
141 /* Try/catch so we can clean up on error */
142 try
143 {
144 list<ovf::VirtualSystem>::const_iterator it;
145 /* Iterate through all virtual systems */
146 for (it = m->pReader->m_llVirtualSystems.begin();
147 it != m->pReader->m_llVirtualSystems.end();
148 ++it)
149 {
150 const ovf::VirtualSystem &vsysThis = *it;
151
152 ComObjPtr<VirtualSystemDescription> pNewDesc;
153 rc = pNewDesc.createObject();
154 if (FAILED(rc)) throw rc;
155 rc = pNewDesc->init();
156 if (FAILED(rc)) throw rc;
157
158 // if the virtual system in OVF had a <vbox:Machine> element, have the
159 // VirtualBox settings code parse that XML now
160 if (vsysThis.pelmVboxMachine)
161 pNewDesc->importVboxMachineXML(*vsysThis.pelmVboxMachine);
162
163 /* Guest OS type */
164 Utf8Str strOsTypeVBox,
165 strCIMOSType = Utf8StrFmt("%RI32", (uint32_t)vsysThis.cimos);
166 convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc);
167 pNewDesc->addEntry(VirtualSystemDescriptionType_OS,
168 "",
169 strCIMOSType,
170 strOsTypeVBox);
171
172 /* VM name */
173 /* If the there isn't any name specified create a default one out of
174 * the OS type */
175 Utf8Str nameVBox = vsysThis.strName;
176 if (nameVBox.isEmpty())
177 nameVBox = strOsTypeVBox;
178 searchUniqueVMName(nameVBox);
179 pNewDesc->addEntry(VirtualSystemDescriptionType_Name,
180 "",
181 vsysThis.strName,
182 nameVBox);
183
184 /* VM Product */
185 if (!vsysThis.strProduct.isEmpty())
186 pNewDesc->addEntry(VirtualSystemDescriptionType_Product,
187 "",
188 vsysThis.strProduct,
189 vsysThis.strProduct);
190
191 /* VM Vendor */
192 if (!vsysThis.strVendor.isEmpty())
193 pNewDesc->addEntry(VirtualSystemDescriptionType_Vendor,
194 "",
195 vsysThis.strVendor,
196 vsysThis.strVendor);
197
198 /* VM Version */
199 if (!vsysThis.strVersion.isEmpty())
200 pNewDesc->addEntry(VirtualSystemDescriptionType_Version,
201 "",
202 vsysThis.strVersion,
203 vsysThis.strVersion);
204
205 /* VM ProductUrl */
206 if (!vsysThis.strProductUrl.isEmpty())
207 pNewDesc->addEntry(VirtualSystemDescriptionType_ProductUrl,
208 "",
209 vsysThis.strProductUrl,
210 vsysThis.strProductUrl);
211
212 /* VM VendorUrl */
213 if (!vsysThis.strVendorUrl.isEmpty())
214 pNewDesc->addEntry(VirtualSystemDescriptionType_VendorUrl,
215 "",
216 vsysThis.strVendorUrl,
217 vsysThis.strVendorUrl);
218
219 /* VM description */
220 if (!vsysThis.strDescription.isEmpty())
221 pNewDesc->addEntry(VirtualSystemDescriptionType_Description,
222 "",
223 vsysThis.strDescription,
224 vsysThis.strDescription);
225
226 /* VM license */
227 if (!vsysThis.strLicenseText.isEmpty())
228 pNewDesc->addEntry(VirtualSystemDescriptionType_License,
229 "",
230 vsysThis.strLicenseText,
231 vsysThis.strLicenseText);
232
233 /* Now that we know the OS type, get our internal defaults based on that. */
234 ComPtr<IGuestOSType> pGuestOSType;
235 rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), pGuestOSType.asOutParam());
236 if (FAILED(rc)) throw rc;
237
238 /* CPU count */
239 ULONG cpuCountVBox = vsysThis.cCPUs;
240 /* Check for the constrains */
241 if (cpuCountVBox > SchemaDefs::MaxCPUCount)
242 {
243 addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."),
244 vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount);
245 cpuCountVBox = SchemaDefs::MaxCPUCount;
246 }
247 if (vsysThis.cCPUs == 0)
248 cpuCountVBox = 1;
249 pNewDesc->addEntry(VirtualSystemDescriptionType_CPU,
250 "",
251 Utf8StrFmt("%RI32", (uint32_t)vsysThis.cCPUs),
252 Utf8StrFmt("%RI32", (uint32_t)cpuCountVBox));
253
254 /* RAM */
255 uint64_t ullMemSizeVBox = vsysThis.ullMemorySize / _1M;
256 /* Check for the constrains */
257 if ( ullMemSizeVBox != 0
258 && ( ullMemSizeVBox < MM_RAM_MIN_IN_MB
259 || ullMemSizeVBox > MM_RAM_MAX_IN_MB
260 )
261 )
262 {
263 addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."),
264 vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
265 ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB);
266 }
267 if (vsysThis.ullMemorySize == 0)
268 {
269 /* If the RAM of the OVF is zero, use our predefined values */
270 ULONG memSizeVBox2;
271 rc = pGuestOSType->COMGETTER(RecommendedRAM)(&memSizeVBox2);
272 if (FAILED(rc)) throw rc;
273 /* VBox stores that in MByte */
274 ullMemSizeVBox = (uint64_t)memSizeVBox2;
275 }
276 pNewDesc->addEntry(VirtualSystemDescriptionType_Memory,
277 "",
278 Utf8StrFmt("%RI64", (uint64_t)vsysThis.ullMemorySize),
279 Utf8StrFmt("%RI64", (uint64_t)ullMemSizeVBox));
280
281 /* Audio */
282 if (!vsysThis.strSoundCardType.isEmpty())
283 /* Currently we set the AC97 always.
284 @todo: figure out the hardware which could be possible */
285 pNewDesc->addEntry(VirtualSystemDescriptionType_SoundCard,
286 "",
287 vsysThis.strSoundCardType,
288 Utf8StrFmt("%RI32", (uint32_t)AudioControllerType_AC97));
289
290#ifdef VBOX_WITH_USB
291 /* USB Controller */
292 if (vsysThis.fHasUsbController)
293 pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", "");
294#endif /* VBOX_WITH_USB */
295
296 /* Network Controller */
297 size_t cEthernetAdapters = vsysThis.llEthernetAdapters.size();
298 if (cEthernetAdapters > 0)
299 {
300 /* Check for the constrains */
301 if (cEthernetAdapters > SchemaDefs::NetworkAdapterCount)
302 addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."),
303 vsysThis.strName.c_str(), cEthernetAdapters, SchemaDefs::NetworkAdapterCount);
304
305 /* Get the default network adapter type for the selected guest OS */
306 NetworkAdapterType_T defaultAdapterVBox = NetworkAdapterType_Am79C970A;
307 rc = pGuestOSType->COMGETTER(AdapterType)(&defaultAdapterVBox);
308 if (FAILED(rc)) throw rc;
309
310 ovf::EthernetAdaptersList::const_iterator itEA;
311 /* Iterate through all abstract networks. We support 8 network
312 * adapters at the maximum, so the first 8 will be added only. */
313 size_t a = 0;
314 for (itEA = vsysThis.llEthernetAdapters.begin();
315 itEA != vsysThis.llEthernetAdapters.end() && a < SchemaDefs::NetworkAdapterCount;
316 ++itEA, ++a)
317 {
318 const ovf::EthernetAdapter &ea = *itEA; // logical network to connect to
319 Utf8Str strNetwork = ea.strNetworkName;
320 // make sure it's one of these two
321 if ( (strNetwork.compare("Null", Utf8Str::CaseInsensitive))
322 && (strNetwork.compare("NAT", Utf8Str::CaseInsensitive))
323 && (strNetwork.compare("Bridged", Utf8Str::CaseInsensitive))
324 && (strNetwork.compare("Internal", Utf8Str::CaseInsensitive))
325 && (strNetwork.compare("HostOnly", Utf8Str::CaseInsensitive))
326 )
327 strNetwork = "Bridged"; // VMware assumes this is the default apparently
328
329 /* Figure out the hardware type */
330 NetworkAdapterType_T nwAdapterVBox = defaultAdapterVBox;
331 if (!ea.strAdapterType.compare("PCNet32", Utf8Str::CaseInsensitive))
332 {
333 /* If the default adapter is already one of the two
334 * PCNet adapters use the default one. If not use the
335 * Am79C970A as fallback. */
336 if (!(defaultAdapterVBox == NetworkAdapterType_Am79C970A ||
337 defaultAdapterVBox == NetworkAdapterType_Am79C973))
338 nwAdapterVBox = NetworkAdapterType_Am79C970A;
339 }
340#ifdef VBOX_WITH_E1000
341 /* VMWare accidentally write this with VirtualCenter 3.5,
342 so make sure in this case always to use the VMWare one */
343 else if (!ea.strAdapterType.compare("E10000", Utf8Str::CaseInsensitive))
344 nwAdapterVBox = NetworkAdapterType_I82545EM;
345 else if (!ea.strAdapterType.compare("E1000", Utf8Str::CaseInsensitive))
346 {
347 /* Check if this OVF was written by VirtualBox */
348 if (Utf8Str(vsysThis.strVirtualSystemType).contains("virtualbox", Utf8Str::CaseInsensitive))
349 {
350 /* If the default adapter is already one of the three
351 * E1000 adapters use the default one. If not use the
352 * I82545EM as fallback. */
353 if (!(defaultAdapterVBox == NetworkAdapterType_I82540EM ||
354 defaultAdapterVBox == NetworkAdapterType_I82543GC ||
355 defaultAdapterVBox == NetworkAdapterType_I82545EM))
356 nwAdapterVBox = NetworkAdapterType_I82540EM;
357 }
358 else
359 /* Always use this one since it's what VMware uses */
360 nwAdapterVBox = NetworkAdapterType_I82545EM;
361 }
362#endif /* VBOX_WITH_E1000 */
363
364 pNewDesc->addEntry(VirtualSystemDescriptionType_NetworkAdapter,
365 "", // ref
366 ea.strNetworkName, // orig
367 Utf8StrFmt("%RI32", (uint32_t)nwAdapterVBox), // conf
368 0,
369 Utf8StrFmt("type=%s", strNetwork.c_str())); // extra conf
370 }
371 }
372
373 /* Floppy Drive */
374 if (vsysThis.fHasFloppyDrive)
375 pNewDesc->addEntry(VirtualSystemDescriptionType_Floppy, "", "", "");
376
377 /* CD Drive */
378 if (vsysThis.fHasCdromDrive)
379 pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, "", "", "");
380
381 /* Hard disk Controller */
382 uint16_t cIDEused = 0;
383 uint16_t cSATAused = 0; NOREF(cSATAused);
384 uint16_t cSCSIused = 0; NOREF(cSCSIused);
385 ovf::ControllersMap::const_iterator hdcIt;
386 /* Iterate through all hard disk controllers */
387 for (hdcIt = vsysThis.mapControllers.begin();
388 hdcIt != vsysThis.mapControllers.end();
389 ++hdcIt)
390 {
391 const ovf::HardDiskController &hdc = hdcIt->second;
392 Utf8Str strControllerID = Utf8StrFmt("%RI32", (uint32_t)hdc.idController);
393
394 switch (hdc.system)
395 {
396 case ovf::HardDiskController::IDE:
397 {
398 /* Check for the constrains */
399 /* @todo: I'm very confused! Are these bits *one* controller or
400 is every port/bus declared as an extra controller. */
401 if (cIDEused < 4)
402 {
403 // @todo: figure out the IDE types
404 /* Use PIIX4 as default */
405 Utf8Str strType = "PIIX4";
406 if (!hdc.strControllerType.compare("PIIX3", Utf8Str::CaseInsensitive))
407 strType = "PIIX3";
408 else if (!hdc.strControllerType.compare("ICH6", Utf8Str::CaseInsensitive))
409 strType = "ICH6";
410 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE,
411 strControllerID,
412 hdc.strControllerType,
413 strType);
414 }
415 else
416 {
417 /* Warn only once */
418 if (cIDEused == 1)
419 addWarning(tr("The virtual \"%s\" system requests support for more than one IDE controller, but VirtualBox has support for only one."),
420 vsysThis.strName.c_str());
421
422 }
423 ++cIDEused;
424 break;
425 }
426
427 case ovf::HardDiskController::SATA:
428 {
429#ifdef VBOX_WITH_AHCI
430 /* Check for the constrains */
431 if (cSATAused < 1)
432 {
433 // @todo: figure out the SATA types
434 /* We only support a plain AHCI controller, so use them always */
435 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA,
436 strControllerID,
437 hdc.strControllerType,
438 "AHCI");
439 }
440 else
441 {
442 /* Warn only once */
443 if (cSATAused == 1)
444 addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"),
445 vsysThis.strName.c_str());
446
447 }
448 ++cSATAused;
449 break;
450#else /* !VBOX_WITH_AHCI */
451 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SATA controller emulation"),
452 vsysThis.strName.c_str());
453#endif /* !VBOX_WITH_AHCI */
454 }
455
456 case ovf::HardDiskController::SCSI:
457 {
458#ifdef VBOX_WITH_LSILOGIC
459 /* Check for the constrains */
460 if (cSCSIused < 1)
461 {
462 Utf8Str hdcController = "LsiLogic";
463 if (!hdc.strControllerType.compare("BusLogic", Utf8Str::CaseInsensitive))
464 hdcController = "BusLogic";
465 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI,
466 strControllerID,
467 hdc.strControllerType,
468 hdcController);
469 }
470 else
471 addWarning(tr("The virtual system \"%s\" requests support for an additional SCSI controller of type \"%s\" with ID %s, but VirtualBox presently supports only one SCSI controller."),
472 vsysThis.strName.c_str(),
473 hdc.strControllerType.c_str(),
474 strControllerID.c_str());
475 ++cSCSIused;
476 break;
477#else /* !VBOX_WITH_LSILOGIC */
478 addWarning(tr("The virtual system \"%s\" requests at least one SATA controller but this version of VirtualBox does not provide a SCSI controller emulation"),
479 vsysThis.strName.c_str());
480#endif /* !VBOX_WITH_LSILOGIC */
481 }
482 }
483 }
484
485 /* Hard disks */
486 if (vsysThis.mapVirtualDisks.size() > 0)
487 {
488 ovf::VirtualDisksMap::const_iterator itVD;
489 /* Iterate through all hard disks ()*/
490 for (itVD = vsysThis.mapVirtualDisks.begin();
491 itVD != vsysThis.mapVirtualDisks.end();
492 ++itVD)
493 {
494 const ovf::VirtualDisk &hd = itVD->second;
495 /* Get the associated disk image */
496 const ovf::DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId];
497
498 // @todo:
499 // - figure out all possible vmdk formats we also support
500 // - figure out if there is a url specifier for vhd already
501 // - we need a url specifier for the vdi format
502 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
503 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
504 )
505 {
506 /* If the href is empty use the VM name as filename */
507 Utf8Str strFilename = di.strHref;
508 if (!strFilename.length())
509 strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str());
510 /* Construct a unique target path */
511 Utf8StrFmt strPath("%s%c%s",
512 strDefaultHardDiskFolder.raw(),
513 RTPATH_DELIMITER,
514 strFilename.c_str());
515 searchUniqueDiskImageFilePath(strPath);
516
517 /* find the description for the hard disk controller
518 * that has the same ID as hd.idController */
519 const VirtualSystemDescriptionEntry *pController;
520 if (!(pController = pNewDesc->findControllerFromID(hd.idController)))
521 throw setError(E_FAIL,
522 tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"),
523 hd.idController,
524 di.strHref.c_str());
525
526 /* controller to attach to, and the bus within that controller */
527 Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16",
528 pController->ulIndex,
529 hd.ulAddressOnParent);
530 pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage,
531 hd.strDiskId,
532 di.strHref,
533 strPath,
534 di.ulSuggestedSizeMB,
535 strExtraConfig);
536 }
537 else
538 throw setError(VBOX_E_FILE_ERROR,
539 tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str()));
540 }
541 }
542
543 m->virtualSystemDescriptions.push_back(pNewDesc);
544 }
545 }
546 catch (HRESULT aRC)
547 {
548 /* On error we clear the list & return */
549 m->virtualSystemDescriptions.clear();
550 rc = aRC;
551 }
552
553 // reset the appliance state
554 alock.acquire();
555 m->state = Data::ApplianceIdle;
556
557 return rc;
558}
559
560/**
561 * Public method implementation.
562 * @param aProgress
563 * @return
564 */
565STDMETHODIMP Appliance::ImportMachines(IProgress **aProgress)
566{
567 CheckComArgOutPointerValid(aProgress);
568
569 AutoCaller autoCaller(this);
570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
571
572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
573
574 // do not allow entering this method if the appliance is busy reading or writing
575 if (!isApplianceIdle())
576 return E_ACCESSDENIED;
577
578 if (!m->pReader)
579 return setError(E_FAIL,
580 tr("Cannot import machines without reading it first (call read() before importMachines())"));
581
582 ComObjPtr<Progress> progress;
583 HRESULT rc = S_OK;
584 try
585 {
586 rc = importImpl(m->locInfo, progress);
587 }
588 catch (HRESULT aRC)
589 {
590 rc = aRC;
591 }
592
593 if (SUCCEEDED(rc))
594 /* Return progress to the caller */
595 progress.queryInterfaceTo(aProgress);
596
597 return rc;
598}
599
600////////////////////////////////////////////////////////////////////////////////
601//
602// Appliance private methods
603//
604////////////////////////////////////////////////////////////////////////////////
605
606/**
607 * Implementation for reading an OVF. This starts a new thread which will call
608 * Appliance::taskThreadImportOrExport() which will then call readFS() or readS3().
609 *
610 * This is in a separate private method because it is used from two locations:
611 *
612 * 1) from the public Appliance::Read().
613 * 2) from Appliance::readS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport().
614 *
615 * @param aLocInfo
616 * @param aProgress
617 * @return
618 */
619HRESULT Appliance::readImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
620{
621 BstrFmt bstrDesc = BstrFmt(tr("Reading appliance '%s'"),
622 aLocInfo.strPath.c_str());
623 HRESULT rc;
624 /* Create the progress object */
625 aProgress.createObject();
626 if (aLocInfo.storageType == VFSType_File)
627 /* 1 operation only */
628 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
629 bstrDesc,
630 TRUE /* aCancelable */);
631 else
632 /* 4/5 is downloading, 1/5 is reading */
633 rc = aProgress->init(mVirtualBox, static_cast<IAppliance*>(this),
634 bstrDesc,
635 TRUE /* aCancelable */,
636 2, // ULONG cOperations,
637 5, // ULONG ulTotalOperationsWeight,
638 BstrFmt(tr("Download appliance '%s'"),
639 aLocInfo.strPath.c_str()), // CBSTR bstrFirstOperationDescription,
640 4); // ULONG ulFirstOperationWeight,
641 if (FAILED(rc)) throw rc;
642
643 /* Initialize our worker task */
644 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Read, aLocInfo, aProgress));
645
646 rc = task->startThread();
647 if (FAILED(rc)) throw rc;
648
649 /* Don't destruct on success */
650 task.release();
651
652 return rc;
653}
654
655/**
656 * Actual worker code for reading an OVF from disk. This is called from Appliance::taskThreadImportOrExport()
657 * and therefore runs on the OVF read worker thread. This runs in two contexts:
658 *
659 * 1) in a first worker thread; in that case, Appliance::Read() called Appliance::readImpl();
660 *
661 * 2) in a second worker thread; in that case, Appliance::Read() called Appliance::readImpl(), which
662 * called Appliance::readS3(), which called Appliance::readImpl(), which then called this.
663 *
664 * @param pTask
665 * @return
666 */
667HRESULT Appliance::readFS(const LocationInfo &locInfo)
668{
669 LogFlowFuncEnter();
670 LogFlowFunc(("Appliance %p\n", this));
671
672 AutoCaller autoCaller(this);
673 if (FAILED(autoCaller.rc())) return autoCaller.rc();
674
675 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
676
677 HRESULT rc = S_OK;
678
679 try
680 {
681 /* Read & parse the XML structure of the OVF file */
682 m->pReader = new ovf::OVFReader(locInfo.strPath);
683 /* Create the SHA1 sum of the OVF file for later validation */
684 char *pszDigest;
685 int vrc = RTSha1Digest(locInfo.strPath.c_str(), &pszDigest);
686 if (RT_FAILURE(vrc))
687 throw setError(VBOX_E_FILE_ERROR,
688 tr("Couldn't calculate SHA1 digest for file '%s' (%Rrc)"),
689 RTPathFilename(locInfo.strPath.c_str()), vrc);
690 m->strOVFSHA1Digest = pszDigest;
691 RTStrFree(pszDigest);
692 }
693 catch(xml::Error &x)
694 {
695 rc = setError(VBOX_E_FILE_ERROR,
696 x.what());
697 }
698 catch(HRESULT aRC)
699 {
700 rc = aRC;
701 }
702
703 LogFlowFunc(("rc=%Rhrc\n", rc));
704 LogFlowFuncLeave();
705
706 return rc;
707}
708
709/**
710 * Worker code for reading OVF from the cloud. This is called from Appliance::taskThreadImportOrExport()
711 * in S3 mode and therefore runs on the OVF read worker thread. This then starts a second worker
712 * thread to create temporary files (see Appliance::readFS()).
713 *
714 * @param pTask
715 * @return
716 */
717HRESULT Appliance::readS3(TaskOVF *pTask)
718{
719 LogFlowFuncEnter();
720 LogFlowFunc(("Appliance %p\n", this));
721
722 AutoCaller autoCaller(this);
723 if (FAILED(autoCaller.rc())) return autoCaller.rc();
724
725 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
726
727 HRESULT rc = S_OK;
728 int vrc = VINF_SUCCESS;
729 RTS3 hS3 = NIL_RTS3;
730 char szOSTmpDir[RTPATH_MAX];
731 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
732 /* The template for the temporary directory created below */
733 char *pszTmpDir;
734 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
735 list< pair<Utf8Str, ULONG> > filesList;
736 Utf8Str strTmpOvf;
737
738 try
739 {
740 /* Extract the bucket */
741 Utf8Str tmpPath = pTask->locInfo.strPath;
742 Utf8Str bucket;
743 parseBucket(tmpPath, bucket);
744
745 /* We need a temporary directory which we can put the OVF file & all
746 * disk images in */
747 vrc = RTDirCreateTemp(pszTmpDir);
748 if (RT_FAILURE(vrc))
749 throw setError(VBOX_E_FILE_ERROR,
750 tr("Cannot create temporary directory '%s'"), pszTmpDir);
751
752 /* The temporary name of the target OVF file */
753 strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
754
755 /* Next we have to download the OVF */
756 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
757 if (RT_FAILURE(vrc))
758 throw setError(VBOX_E_IPRT_ERROR,
759 tr("Cannot create S3 service handler"));
760 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
761
762 /* Get it */
763 char *pszFilename = RTPathFilename(strTmpOvf.c_str());
764 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strTmpOvf.c_str());
765 if (RT_FAILURE(vrc))
766 {
767 if (vrc == VERR_S3_CANCELED)
768 throw S_OK; /* todo: !!!!!!!!!!!!! */
769 else if (vrc == VERR_S3_ACCESS_DENIED)
770 throw setError(E_ACCESSDENIED,
771 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
772 else if (vrc == VERR_S3_NOT_FOUND)
773 throw setError(VBOX_E_FILE_ERROR,
774 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
775 else
776 throw setError(VBOX_E_IPRT_ERROR,
777 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
778 }
779
780 /* Close the connection early */
781 RTS3Destroy(hS3);
782 hS3 = NIL_RTS3;
783
784 if (!pTask->pProgress.isNull())
785 pTask->pProgress->SetNextOperation(Bstr(tr("Reading")), 1);
786
787 /* Prepare the temporary reading of the OVF */
788 ComObjPtr<Progress> progress;
789 LocationInfo li;
790 li.strPath = strTmpOvf;
791 /* Start the reading from the fs */
792 rc = readImpl(li, progress);
793 if (FAILED(rc)) throw rc;
794
795 /* Unlock the appliance for the reading thread */
796 appLock.release();
797 /* Wait until the reading is done, but report the progress back to the
798 caller */
799 ComPtr<IProgress> progressInt(progress);
800 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
801
802 /* Again lock the appliance for the next steps */
803 appLock.acquire();
804 }
805 catch(HRESULT aRC)
806 {
807 rc = aRC;
808 }
809 /* Cleanup */
810 RTS3Destroy(hS3);
811 /* Delete all files which where temporary created */
812 if (RTPathExists(strTmpOvf.c_str()))
813 {
814 vrc = RTFileDelete(strTmpOvf.c_str());
815 if (RT_FAILURE(vrc))
816 rc = setError(VBOX_E_FILE_ERROR,
817 tr("Cannot delete file '%s' (%Rrc)"), strTmpOvf.c_str(), vrc);
818 }
819 /* Delete the temporary directory */
820 if (RTPathExists(pszTmpDir))
821 {
822 vrc = RTDirRemove(pszTmpDir);
823 if (RT_FAILURE(vrc))
824 rc = setError(VBOX_E_FILE_ERROR,
825 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
826 }
827 if (pszTmpDir)
828 RTStrFree(pszTmpDir);
829
830 LogFlowFunc(("rc=%Rhrc\n", rc));
831 LogFlowFuncLeave();
832
833 return rc;
834}
835
836/**
837 * Helper that converts VirtualSystem attachment values into VirtualBox attachment values.
838 * Throws HRESULT values on errors!
839 *
840 * @param hdc in: the HardDiskController structure to attach to.
841 * @param ulAddressOnParent in: the AddressOnParent parameter from OVF.
842 * @param controllerType out: the name of the hard disk controller to attach to (e.g. "IDE Controller").
843 * @param lChannel out: the channel (controller port) of the controller to attach to.
844 * @param lDevice out: the device number to attach to.
845 */
846void Appliance::convertDiskAttachmentValues(const ovf::HardDiskController &hdc,
847 uint32_t ulAddressOnParent,
848 Bstr &controllerType,
849 int32_t &lChannel,
850 int32_t &lDevice)
851{
852 switch (hdc.system)
853 {
854 case ovf::HardDiskController::IDE:
855 // For the IDE bus, the channel parameter can be either 0 or 1, to specify the primary
856 // or secondary IDE controller, respectively. For the primary controller of the IDE bus,
857 // the device number can be either 0 or 1, to specify the master or the slave device,
858 // respectively. For the secondary IDE controller, the device number is always 1 because
859 // the master device is reserved for the CD-ROM drive.
860 controllerType = Bstr("IDE Controller");
861 switch (ulAddressOnParent)
862 {
863 case 0: // master
864 if (hdc.ulAddress == 1)
865 {
866 // IDE controller has address 1: then it was exported from VMware and is the secondary controller
867 lChannel = (long)1;
868 lDevice = (long)0;
869 }
870 else // interpret this as primary master
871 {
872 lChannel = (long)0;
873 lDevice = (long)0;
874 }
875 break;
876
877 case 1: // slave
878 if (hdc.ulAddress == 1)
879 {
880 // IDE controller has address 1: then it was exported from VMware and is the secondary controller
881 lChannel = (long)1;
882 lDevice = (long)1;
883 }
884 else // interpret this as primary slave
885 {
886 lChannel = (long)0;
887 lDevice = (long)1;
888 }
889 break;
890
891 // used by older VBox exports
892 case 2: // interpret this as secondary master
893 lChannel = (long)1;
894 lDevice = (long)0;
895 break;
896
897 // used by older VBox exports
898 case 3: // interpret this as secondary slave
899 lChannel = (long)1;
900 lDevice = (long)1;
901 break;
902
903 default:
904 throw setError(VBOX_E_NOT_SUPPORTED,
905 tr("Invalid channel %RI16 specified; IDE controllers support only 0, 1 or 2"), ulAddressOnParent);
906 break;
907 }
908 break;
909
910 case ovf::HardDiskController::SATA:
911 controllerType = Bstr("SATA Controller");
912 lChannel = (long)ulAddressOnParent;
913 lDevice = (long)0;
914 break;
915
916 case ovf::HardDiskController::SCSI:
917 controllerType = Bstr("SCSI Controller");
918 lChannel = (long)ulAddressOnParent;
919 lDevice = (long)0;
920 break;
921
922 default: break;
923 }
924}
925
926/**
927 * Implementation for importing OVF data into VirtualBox. This starts a new thread which will call
928 * Appliance::taskThreadImportOrExport().
929 *
930 * This is in a separate private method because it is used from two locations:
931 *
932 * 1) from the public Appliance::ImportMachines().
933 * 2) from Appliance::importS3(), which got called from a previous instance of Appliance::taskThreadImportOrExport().
934 *
935 * @param aLocInfo
936 * @param aProgress
937 * @return
938 */
939HRESULT Appliance::importImpl(const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress)
940{
941 Bstr progressDesc = BstrFmt(tr("Importing appliance '%s'"),
942 aLocInfo.strPath.c_str());
943 HRESULT rc = S_OK;
944
945 rc = setUpProgress(aProgress,
946 progressDesc,
947 (aLocInfo.storageType == VFSType_File) ? Regular : ImportS3);
948 if (FAILED(rc)) throw rc;
949
950 /* Initialize our worker task */
951 std::auto_ptr<TaskOVF> task(new TaskOVF(this, TaskOVF::Import, aLocInfo, aProgress));
952
953 rc = task->startThread();
954 if (FAILED(rc)) throw rc;
955
956 /* Don't destruct on success */
957 task.release();
958
959 return rc;
960}
961
962/**
963 * Used by Appliance::importMachineGeneric() to store
964 * input parameters and rollback information.
965 */
966struct Appliance::ImportStack
967{
968 // input pointers
969 const LocationInfo &locInfo; // ptr to location info from Appliance::importFS()
970 Utf8Str strSourceDir; // directory where source files reside
971 const ovf::DiskImagesMap &mapDisks; // ptr to disks map in OVF
972 ComObjPtr<Progress> &pProgress; // progress object passed into Appliance::importFS()
973
974 // session (not initially created)
975 ComPtr<ISession> pSession; // session opened in Appliance::importFS() for machine manipulation
976 bool fSessionOpen; // true if the pSession is currently open and needs closing
977
978 // a list of images that we created/imported; this is initially empty
979 // and will be cleaned up on errors
980 list<MyHardDiskAttachment> llHardDiskAttachments; // disks that were attached
981 list< ComPtr<IMedium> > llHardDisksCreated; // media that were created
982 list<Bstr> llMachinesRegistered; // machines that were registered; list of string UUIDs
983
984 ImportStack(const LocationInfo &aLocInfo,
985 const ovf::DiskImagesMap &aMapDisks,
986 ComObjPtr<Progress> &aProgress)
987 : locInfo(aLocInfo),
988 mapDisks(aMapDisks),
989 pProgress(aProgress),
990 fSessionOpen(false)
991 {
992 // disk images have to be on the same place as the OVF file. So
993 // strip the filename out of the full file path
994 strSourceDir = aLocInfo.strPath;
995 strSourceDir.stripFilename();
996 }
997};
998
999/**
1000 * Checks if a manifest file exists in the given location and, if so, verifies
1001 * that the relevant files (the OVF XML and the disks referenced by it, as
1002 * represented by the VirtualSystemDescription instances contained in this appliance)
1003 * match it. Requires a previous read() and interpret().
1004 *
1005 * @param locInfo
1006 * @param reader
1007 * @return
1008 */
1009HRESULT Appliance::manifestVerify(const LocationInfo &locInfo,
1010 const ovf::OVFReader &reader)
1011{
1012 HRESULT rc = S_OK;
1013
1014 Utf8Str strMfFile = manifestFileName(locInfo.strPath);
1015 if (RTPathExists(strMfFile.c_str()))
1016 {
1017 list<Utf8Str> filesList;
1018 Utf8Str strSrcDir(locInfo.strPath);
1019 strSrcDir.stripFilename();
1020 // add every disks of every virtual system to an internal list
1021 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
1022 for (it = m->virtualSystemDescriptions.begin();
1023 it != m->virtualSystemDescriptions.end();
1024 ++it)
1025 {
1026 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
1027 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1028 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
1029 for (itH = avsdeHDs.begin();
1030 itH != avsdeHDs.end();
1031 ++itH)
1032 {
1033 VirtualSystemDescriptionEntry *vsdeHD = *itH;
1034 // find the disk from the OVF's disk list
1035 ovf::DiskImagesMap::const_iterator itDiskImage = reader.m_mapDisks.find(vsdeHD->strRef);
1036 const ovf::DiskImage &di = itDiskImage->second;
1037 Utf8StrFmt strSrcFilePath("%s%c%s", strSrcDir.c_str(), RTPATH_DELIMITER, di.strHref.c_str());
1038 filesList.push_back(strSrcFilePath);
1039 }
1040 }
1041
1042 // create the test list
1043 PRTMANIFESTTEST pTestList = (PRTMANIFESTTEST)RTMemAllocZ(sizeof(RTMANIFESTTEST) * (filesList.size() + 1));
1044 pTestList[0].pszTestFile = (char*)locInfo.strPath.c_str();
1045 pTestList[0].pszTestDigest = (char*)m->strOVFSHA1Digest.c_str();
1046 int vrc = VINF_SUCCESS;
1047 size_t i = 1;
1048 list<Utf8Str>::const_iterator it1;
1049 for (it1 = filesList.begin();
1050 it1 != filesList.end();
1051 ++it1, ++i)
1052 {
1053 char* pszDigest;
1054 vrc = RTSha1Digest((*it1).c_str(), &pszDigest);
1055 pTestList[i].pszTestFile = (char*)(*it1).c_str();
1056 pTestList[i].pszTestDigest = pszDigest;
1057 }
1058
1059 // this call can take a very long time
1060 size_t cIndexOnError;
1061 vrc = RTManifestVerify(strMfFile.c_str(),
1062 pTestList,
1063 filesList.size() + 1,
1064 &cIndexOnError);
1065
1066 // clean up
1067 for (size_t j = 1;
1068 j < filesList.size();
1069 ++j)
1070 RTStrFree(pTestList[j].pszTestDigest);
1071 RTMemFree(pTestList);
1072
1073 if (vrc == VERR_MANIFEST_DIGEST_MISMATCH)
1074 rc = setError(VBOX_E_FILE_ERROR,
1075 tr("The SHA1 digest of '%s' does not match the one in '%s'"),
1076 RTPathFilename(pTestList[cIndexOnError].pszTestFile),
1077 RTPathFilename(strMfFile.c_str()));
1078 else if (RT_FAILURE(vrc))
1079 rc = setError(VBOX_E_FILE_ERROR,
1080 tr("Could not verify the content of '%s' against the available files (%Rrc)"),
1081 RTPathFilename(strMfFile.c_str()),
1082 vrc);
1083 }
1084
1085 return rc;
1086}
1087
1088/**
1089 * Actual worker code for importing OVF data into VirtualBox. This is called from Appliance::taskThreadImportOrExport()
1090 * and therefore runs on the OVF import worker thread. This runs in two contexts:
1091 *
1092 * 1) in a first worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl();
1093 *
1094 * 2) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which
1095 * called Appliance::importS3(), which called Appliance::importImpl(), which then called this.
1096 *
1097 * @param pTask
1098 * @return
1099 */
1100HRESULT Appliance::importFS(const LocationInfo &locInfo,
1101 ComObjPtr<Progress> &pProgress)
1102{
1103 LogFlowFuncEnter();
1104 LogFlowFunc(("Appliance %p\n", this));
1105
1106 AutoCaller autoCaller(this);
1107 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1108
1109 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
1110
1111 if (!isApplianceIdle())
1112 return E_ACCESSDENIED;
1113
1114 Assert(!pProgress.isNull());
1115
1116 // Change the appliance state so we can safely leave the lock while doing time-consuming
1117 // disk imports; also the below method calls do all kinds of locking which conflicts with
1118 // the appliance object lock
1119 m->state = Data::ApplianceImporting;
1120 appLock.release();
1121
1122 HRESULT rc = S_OK;
1123
1124 const ovf::OVFReader &reader = *m->pReader;
1125 // this is safe to access because this thread only gets started
1126 // if pReader != NULL
1127
1128 // rollback for errors:
1129 ImportStack stack(locInfo, reader.m_mapDisks, pProgress);
1130
1131 try
1132 {
1133 // if a manifest file exists, verify the content; we then need all files which are referenced by the OVF & the OVF itself
1134 rc = manifestVerify(locInfo, reader);
1135 if (FAILED(rc)) throw rc;
1136
1137 // create a session for the machine + disks we manipulate below
1138 rc = stack.pSession.createInprocObject(CLSID_Session);
1139 if (FAILED(rc)) throw rc;
1140
1141 list<ovf::VirtualSystem>::const_iterator it;
1142 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it1;
1143 /* Iterate through all virtual systems of that appliance */
1144 size_t i = 0;
1145 for (it = reader.m_llVirtualSystems.begin(),
1146 it1 = m->virtualSystemDescriptions.begin();
1147 it != reader.m_llVirtualSystems.end();
1148 ++it, ++it1, ++i)
1149 {
1150 const ovf::VirtualSystem &vsysThis = *it;
1151 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it1);
1152
1153 ComPtr<IMachine> pNewMachine;
1154
1155 // there are two ways in which we can create a vbox machine from OVF:
1156 // -- either this OVF was written by vbox 3.2 or later, in which case there is a <vbox:Machine> element
1157 // in the <VirtualSystem>; then the VirtualSystemDescription::Data has a settings::MachineConfigFile
1158 // with all the machine config pretty-parsed;
1159 // -- or this is an OVF from an older vbox or an external source, and then we need to translate the
1160 // VirtualSystemDescriptionEntry and do import work
1161
1162 // @todo r=dj make this selection configurable at run-time, and from the GUI as well
1163
1164 if (vsdescThis->m->pConfig)
1165 importVBoxMachine(vsdescThis, pNewMachine, stack);
1166 else
1167 importMachineGeneric(vsysThis, vsdescThis, pNewMachine, stack);
1168
1169 } // for (it = pAppliance->m->llVirtualSystems.begin() ...
1170 }
1171 catch (HRESULT rc2)
1172 {
1173 rc = rc2;
1174 }
1175
1176 if (FAILED(rc))
1177 {
1178 // with _whatever_ error we've had, do a complete roll-back of
1179 // machines and disks we've created; unfortunately this is
1180 // not so trivially done...
1181
1182 HRESULT rc2;
1183 // detach all hard disks from all machines we created
1184 list<MyHardDiskAttachment>::iterator itM;
1185 for (itM = stack.llHardDiskAttachments.begin();
1186 itM != stack.llHardDiskAttachments.end();
1187 ++itM)
1188 {
1189 const MyHardDiskAttachment &mhda = *itM;
1190 Bstr bstrUuid(mhda.bstrUuid); // make a copy, Windows can't handle const Bstr
1191 rc2 = mVirtualBox->OpenSession(stack.pSession, bstrUuid);
1192 if (SUCCEEDED(rc2))
1193 {
1194 ComPtr<IMachine> sMachine;
1195 rc2 = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam());
1196 if (SUCCEEDED(rc2))
1197 {
1198 rc2 = sMachine->DetachDevice(Bstr(mhda.controllerType), mhda.lChannel, mhda.lDevice);
1199 rc2 = sMachine->SaveSettings();
1200 }
1201 stack.pSession->Close();
1202 }
1203 }
1204
1205 // now clean up all hard disks we created
1206 list< ComPtr<IMedium> >::iterator itHD;
1207 for (itHD = stack.llHardDisksCreated.begin();
1208 itHD != stack.llHardDisksCreated.end();
1209 ++itHD)
1210 {
1211 ComPtr<IMedium> pDisk = *itHD;
1212 ComPtr<IProgress> pProgress2;
1213 rc2 = pDisk->DeleteStorage(pProgress2.asOutParam());
1214 rc2 = pProgress2->WaitForCompletion(-1);
1215 }
1216
1217 // finally, deregister and remove all machines
1218 list<Bstr>::iterator itID;
1219 for (itID = stack.llMachinesRegistered.begin();
1220 itID != stack.llMachinesRegistered.end();
1221 ++itID)
1222 {
1223 Bstr bstrGuid = *itID; // make a copy, Windows can't handle const Bstr
1224 ComPtr<IMachine> failedMachine;
1225 rc2 = mVirtualBox->UnregisterMachine(bstrGuid, failedMachine.asOutParam());
1226 if (SUCCEEDED(rc2))
1227 rc2 = failedMachine->DeleteSettings();
1228 }
1229 }
1230
1231 // restore the appliance state
1232 appLock.acquire();
1233 m->state = Data::ApplianceIdle;
1234
1235 LogFlowFunc(("rc=%Rhrc\n", rc));
1236 LogFlowFuncLeave();
1237
1238 return rc;
1239}
1240
1241/**
1242 * Imports one disk image. This is common code shared between
1243 * -- importMachineGeneric() for the OVF case; in that case the information comes from
1244 * the OVF virtual systems;
1245 * -- importVBoxMachine(); in that case, the information comes from the <vbox:Machine>
1246 * tag.
1247 *
1248 * Both ways of describing machines use the OVF disk references section, so in both cases
1249 * the caller needs to pass in the ovf::DiskImage structure from ovfreader.cpp.
1250 *
1251 * As a result, in both cases, if di.strHref is empty, we create a new disk as per the OVF
1252 * spec, even though this cannot really happen in the vbox:Machine case since such data
1253 * would never have been exported.
1254 *
1255 * This advances stack.pProgress by one operation with the disk's weight.
1256 *
1257 * @param di ovfreader.cpp structure describing the disk image from the OVF that is to be imported
1258 * @param ulSizeMB Size of the disk image (for progress reporting)
1259 * @param strTargetPath Where to create the target image.
1260 * @param pTargetHD out: The newly created target disk. This also gets pushed on stack.llHardDisksCreated for cleanup.
1261 * @param stack
1262 */
1263void Appliance::importOneDiskImage(const ovf::DiskImage &di,
1264 const Utf8Str &strTargetPath,
1265 ComPtr<IMedium> &pTargetHD,
1266 ImportStack &stack)
1267{
1268 ComPtr<IMedium> pSourceHD;
1269 bool fSourceHdNeedsClosing = false;
1270
1271 try
1272 {
1273 // destination file must not exist
1274 if ( strTargetPath.isEmpty()
1275 || RTPathExists(strTargetPath.c_str())
1276 )
1277 /* This isn't allowed */
1278 throw setError(VBOX_E_FILE_ERROR,
1279 tr("Destination file '%s' exists"),
1280 strTargetPath.c_str());
1281
1282 const Utf8Str &strSourceOVF = di.strHref;
1283
1284 // Make sure target directory exists
1285 HRESULT rc = VirtualBox::ensureFilePathExists(strTargetPath.c_str());
1286 if (FAILED(rc))
1287 throw rc;
1288
1289 // subprogress object for hard disk
1290 ComPtr<IProgress> pProgress2;
1291
1292 /* If strHref is empty we have to create a new file */
1293 if (strSourceOVF.isEmpty())
1294 {
1295 // which format to use?
1296 Bstr srcFormat = L"VDI";
1297 if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive)
1298 || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive)
1299 )
1300 srcFormat = L"VMDK";
1301 // create an empty hard disk
1302 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(strTargetPath), pTargetHD.asOutParam());
1303 if (FAILED(rc)) throw rc;
1304
1305 // create a dynamic growing disk image with the given capacity
1306 rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, pProgress2.asOutParam());
1307 if (FAILED(rc)) throw rc;
1308
1309 // advance to the next operation
1310 stack.pProgress->SetNextOperation(BstrFmt(tr("Creating virtual disk image '%s'"), strTargetPath.c_str()),
1311 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally
1312 }
1313 else
1314 {
1315 // construct source file path
1316 Utf8StrFmt strSrcFilePath("%s%c%s", stack.strSourceDir.c_str(), RTPATH_DELIMITER, strSourceOVF.c_str());
1317 // source path must exist
1318 if (!RTPathExists(strSrcFilePath.c_str()))
1319 throw setError(VBOX_E_FILE_ERROR,
1320 tr("Source virtual disk image file '%s' doesn't exist"),
1321 strSrcFilePath.c_str());
1322
1323 // Clone the disk image (this is necessary cause the id has
1324 // to be recreated for the case the same hard disk is
1325 // attached already from a previous import)
1326
1327 // First open the existing disk image
1328 rc = mVirtualBox->OpenHardDisk(Bstr(strSrcFilePath),
1329 AccessMode_ReadOnly,
1330 false,
1331 NULL,
1332 false,
1333 NULL,
1334 pSourceHD.asOutParam());
1335 if (FAILED(rc)) throw rc;
1336 fSourceHdNeedsClosing = true;
1337
1338 /* We need the format description of the source disk image */
1339 Bstr srcFormat;
1340 rc = pSourceHD->COMGETTER(Format)(srcFormat.asOutParam());
1341 if (FAILED(rc)) throw rc;
1342 /* Create a new hard disk interface for the destination disk image */
1343 rc = mVirtualBox->CreateHardDisk(srcFormat, Bstr(strTargetPath), pTargetHD.asOutParam());
1344 if (FAILED(rc)) throw rc;
1345 /* Clone the source disk image */
1346 rc = pSourceHD->CloneTo(pTargetHD, MediumVariant_Standard, NULL, pProgress2.asOutParam());
1347 if (FAILED(rc)) throw rc;
1348
1349 /* Advance to the next operation */
1350 stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), strSrcFilePath.c_str()),
1351 di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally);
1352 }
1353
1354 // now wait for the background disk operation to complete; this throws HRESULTs on error
1355 waitForAsyncProgress(stack.pProgress, pProgress2);
1356
1357 if (fSourceHdNeedsClosing)
1358 {
1359 rc = pSourceHD->Close();
1360 if (FAILED(rc)) throw rc;
1361 fSourceHdNeedsClosing = false;
1362 }
1363
1364 stack.llHardDisksCreated.push_back(pTargetHD);
1365 }
1366 catch (...)
1367 {
1368 if (fSourceHdNeedsClosing)
1369 pSourceHD->Close();
1370
1371 throw;
1372 }
1373}
1374
1375/**
1376 * Imports one OVF virtual system (described by the given ovf::VirtualSystem and VirtualSystemDescription)
1377 * into VirtualBox by creating an IMachine instance, which is returned.
1378 *
1379 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean
1380 * up any leftovers from this function. For this, the given ImportStack instance has received information
1381 * about what needs cleaning up (to support rollback).
1382 *
1383 * @param locInfo
1384 * @param vsysThis
1385 * @param vsdescThis
1386 * @param pNewMachine
1387 * @param stack
1388 */
1389void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis,
1390 ComObjPtr<VirtualSystemDescription> &vsdescThis,
1391 ComPtr<IMachine> &pNewMachine,
1392 ImportStack &stack)
1393{
1394 /* Guest OS type */
1395 std::list<VirtualSystemDescriptionEntry*> vsdeOS;
1396 vsdeOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS);
1397 if (vsdeOS.size() < 1)
1398 throw setError(VBOX_E_FILE_ERROR,
1399 tr("Missing guest OS type"));
1400 const Utf8Str &strOsTypeVBox = vsdeOS.front()->strVbox;
1401
1402 /* Now that we know the base system get our internal defaults based on that. */
1403 ComPtr<IGuestOSType> osType;
1404 HRESULT rc = mVirtualBox->GetGuestOSType(Bstr(strOsTypeVBox), osType.asOutParam());
1405 if (FAILED(rc)) throw rc;
1406
1407 /* Create the machine */
1408 /* First get the name */
1409 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
1410 if (vsdeName.size() < 1)
1411 throw setError(VBOX_E_FILE_ERROR,
1412 tr("Missing VM name"));
1413 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
1414 rc = mVirtualBox->CreateMachine(Bstr(strNameVBox),
1415 Bstr(strOsTypeVBox),
1416 NULL,
1417 NULL,
1418 FALSE,
1419 pNewMachine.asOutParam());
1420 if (FAILED(rc)) throw rc;
1421
1422 // and the description
1423 std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description);
1424 if (vsdeDescription.size())
1425 {
1426 const Utf8Str &strDescription = vsdeDescription.front()->strVbox;
1427 rc = pNewMachine->COMSETTER(Description)(Bstr(strDescription));
1428 if (FAILED(rc)) throw rc;
1429 }
1430
1431 /* CPU count */
1432 std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU);
1433 ComAssertMsgThrow(vsdeCPU.size() == 1, ("CPU count missing"), E_FAIL);
1434 const Utf8Str &cpuVBox = vsdeCPU.front()->strVbox;
1435 ULONG tmpCount = (ULONG)RTStrToUInt64(cpuVBox.c_str());
1436 rc = pNewMachine->COMSETTER(CPUCount)(tmpCount);
1437 if (FAILED(rc)) throw rc;
1438 bool fEnableIOApic = false;
1439 /* We need HWVirt & IO-APIC if more than one CPU is requested */
1440 if (tmpCount > 1)
1441 {
1442 rc = pNewMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, TRUE);
1443 if (FAILED(rc)) throw rc;
1444
1445 fEnableIOApic = true;
1446 }
1447
1448 /* RAM */
1449 std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory);
1450 ComAssertMsgThrow(vsdeRAM.size() == 1, ("RAM size missing"), E_FAIL);
1451 const Utf8Str &memoryVBox = vsdeRAM.front()->strVbox;
1452 ULONG tt = (ULONG)RTStrToUInt64(memoryVBox.c_str());
1453 rc = pNewMachine->COMSETTER(MemorySize)(tt);
1454 if (FAILED(rc)) throw rc;
1455
1456 /* VRAM */
1457 /* Get the recommended VRAM for this guest OS type */
1458 ULONG vramVBox;
1459 rc = osType->COMGETTER(RecommendedVRAM)(&vramVBox);
1460 if (FAILED(rc)) throw rc;
1461
1462 /* Set the VRAM */
1463 rc = pNewMachine->COMSETTER(VRAMSize)(vramVBox);
1464 if (FAILED(rc)) throw rc;
1465
1466 // I/O APIC: Generic OVF has no setting for this. Enable it if we
1467 // import a Windows VM because if if Windows was installed without IOAPIC,
1468 // it will not mind finding an one later on, but if Windows was installed
1469 // _with_ an IOAPIC, it will bluescreen if it's not found
1470 if (!fEnableIOApic)
1471 {
1472 Bstr bstrFamilyId;
1473 rc = osType->COMGETTER(FamilyId)(bstrFamilyId.asOutParam());
1474 if (FAILED(rc)) throw rc;
1475 if (bstrFamilyId == "Windows")
1476 fEnableIOApic = true;
1477 }
1478
1479 if (fEnableIOApic)
1480 {
1481 ComPtr<IBIOSSettings> pBIOSSettings;
1482 rc = pNewMachine->COMGETTER(BIOSSettings)(pBIOSSettings.asOutParam());
1483 if (FAILED(rc)) throw rc;
1484
1485 rc = pBIOSSettings->COMSETTER(IOAPICEnabled)(TRUE);
1486 if (FAILED(rc)) throw rc;
1487 }
1488
1489 /* Audio Adapter */
1490 std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard);
1491 /* @todo: we support one audio adapter only */
1492 if (vsdeAudioAdapter.size() > 0)
1493 {
1494 const Utf8Str& audioAdapterVBox = vsdeAudioAdapter.front()->strVbox;
1495 if (audioAdapterVBox.compare("null", Utf8Str::CaseInsensitive) != 0)
1496 {
1497 uint32_t audio = RTStrToUInt32(audioAdapterVBox.c_str());
1498 ComPtr<IAudioAdapter> audioAdapter;
1499 rc = pNewMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam());
1500 if (FAILED(rc)) throw rc;
1501 rc = audioAdapter->COMSETTER(Enabled)(true);
1502 if (FAILED(rc)) throw rc;
1503 rc = audioAdapter->COMSETTER(AudioController)(static_cast<AudioControllerType_T>(audio));
1504 if (FAILED(rc)) throw rc;
1505 }
1506 }
1507
1508#ifdef VBOX_WITH_USB
1509 /* USB Controller */
1510 std::list<VirtualSystemDescriptionEntry*> vsdeUSBController = vsdescThis->findByType(VirtualSystemDescriptionType_USBController);
1511 // USB support is enabled if there's at least one such entry; to disable USB support,
1512 // the type of the USB item would have been changed to "ignore"
1513 bool fUSBEnabled = vsdeUSBController.size() > 0;
1514
1515 ComPtr<IUSBController> usbController;
1516 rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam());
1517 if (FAILED(rc)) throw rc;
1518 rc = usbController->COMSETTER(Enabled)(fUSBEnabled);
1519 if (FAILED(rc)) throw rc;
1520#endif /* VBOX_WITH_USB */
1521
1522 /* Change the network adapters */
1523 std::list<VirtualSystemDescriptionEntry*> vsdeNW = vsdescThis->findByType(VirtualSystemDescriptionType_NetworkAdapter);
1524 if (vsdeNW.size() == 0)
1525 {
1526 /* No network adapters, so we have to disable our default one */
1527 ComPtr<INetworkAdapter> nwVBox;
1528 rc = pNewMachine->GetNetworkAdapter(0, nwVBox.asOutParam());
1529 if (FAILED(rc)) throw rc;
1530 rc = nwVBox->COMSETTER(Enabled)(false);
1531 if (FAILED(rc)) throw rc;
1532 }
1533 else if (vsdeNW.size() > SchemaDefs::NetworkAdapterCount)
1534 throw setError(VBOX_E_FILE_ERROR,
1535 tr("Too many network adapters: OVF requests %d network adapters, but VirtualBox only supports %d"),
1536 vsdeNW.size(), SchemaDefs::NetworkAdapterCount);
1537 else
1538 {
1539 list<VirtualSystemDescriptionEntry*>::const_iterator nwIt;
1540 size_t a = 0;
1541 for (nwIt = vsdeNW.begin();
1542 nwIt != vsdeNW.end();
1543 ++nwIt, ++a)
1544 {
1545 const VirtualSystemDescriptionEntry* pvsys = *nwIt;
1546
1547 const Utf8Str &nwTypeVBox = pvsys->strVbox;
1548 uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str());
1549 ComPtr<INetworkAdapter> pNetworkAdapter;
1550 rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam());
1551 if (FAILED(rc)) throw rc;
1552 /* Enable the network card & set the adapter type */
1553 rc = pNetworkAdapter->COMSETTER(Enabled)(true);
1554 if (FAILED(rc)) throw rc;
1555 rc = pNetworkAdapter->COMSETTER(AdapterType)(static_cast<NetworkAdapterType_T>(tt1));
1556 if (FAILED(rc)) throw rc;
1557
1558 // default is NAT; change to "bridged" if extra conf says so
1559 if (!pvsys->strExtraConfig.compare("type=Bridged", Utf8Str::CaseInsensitive))
1560 {
1561 /* Attach to the right interface */
1562 rc = pNetworkAdapter->AttachToBridgedInterface();
1563 if (FAILED(rc)) throw rc;
1564 ComPtr<IHost> host;
1565 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
1566 if (FAILED(rc)) throw rc;
1567 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1568 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1569 if (FAILED(rc)) throw rc;
1570 // We search for the first host network interface which
1571 // is usable for bridged networking
1572 for (size_t j = 0;
1573 j < nwInterfaces.size();
1574 ++j)
1575 {
1576 HostNetworkInterfaceType_T itype;
1577 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
1578 if (FAILED(rc)) throw rc;
1579 if (itype == HostNetworkInterfaceType_Bridged)
1580 {
1581 Bstr name;
1582 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
1583 if (FAILED(rc)) throw rc;
1584 /* Set the interface name to attach to */
1585 pNetworkAdapter->COMSETTER(HostInterface)(name);
1586 if (FAILED(rc)) throw rc;
1587 break;
1588 }
1589 }
1590 }
1591 /* Next test for host only interfaces */
1592 else if (!pvsys->strExtraConfig.compare("type=HostOnly", Utf8Str::CaseInsensitive))
1593 {
1594 /* Attach to the right interface */
1595 rc = pNetworkAdapter->AttachToHostOnlyInterface();
1596 if (FAILED(rc)) throw rc;
1597 ComPtr<IHost> host;
1598 rc = mVirtualBox->COMGETTER(Host)(host.asOutParam());
1599 if (FAILED(rc)) throw rc;
1600 com::SafeIfaceArray<IHostNetworkInterface> nwInterfaces;
1601 rc = host->COMGETTER(NetworkInterfaces)(ComSafeArrayAsOutParam(nwInterfaces));
1602 if (FAILED(rc)) throw rc;
1603 // We search for the first host network interface which
1604 // is usable for host only networking
1605 for (size_t j = 0;
1606 j < nwInterfaces.size();
1607 ++j)
1608 {
1609 HostNetworkInterfaceType_T itype;
1610 rc = nwInterfaces[j]->COMGETTER(InterfaceType)(&itype);
1611 if (FAILED(rc)) throw rc;
1612 if (itype == HostNetworkInterfaceType_HostOnly)
1613 {
1614 Bstr name;
1615 rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam());
1616 if (FAILED(rc)) throw rc;
1617 /* Set the interface name to attach to */
1618 pNetworkAdapter->COMSETTER(HostInterface)(name);
1619 if (FAILED(rc)) throw rc;
1620 break;
1621 }
1622 }
1623 }
1624 }
1625 }
1626
1627 // IDE Hard disk controller
1628 std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE);
1629 // In OVF (at least VMware's version of it), an IDE controller has two ports, so VirtualBox's single IDE controller
1630 // with two channels and two ports each counts as two OVF IDE controllers -- so we accept one or two such IDE controllers
1631 uint32_t cIDEControllers = vsdeHDCIDE.size();
1632 if (cIDEControllers > 2)
1633 throw setError(VBOX_E_FILE_ERROR,
1634 tr("Too many IDE controllers in OVF; import facility only supports two"));
1635 if (vsdeHDCIDE.size() > 0)
1636 {
1637 // one or two IDE controllers present in OVF: add one VirtualBox controller
1638 ComPtr<IStorageController> pController;
1639 rc = pNewMachine->AddStorageController(Bstr("IDE Controller"), StorageBus_IDE, pController.asOutParam());
1640 if (FAILED(rc)) throw rc;
1641
1642 const char *pcszIDEType = vsdeHDCIDE.front()->strVbox.c_str();
1643 if (!strcmp(pcszIDEType, "PIIX3"))
1644 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3);
1645 else if (!strcmp(pcszIDEType, "PIIX4"))
1646 rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX4);
1647 else if (!strcmp(pcszIDEType, "ICH6"))
1648 rc = pController->COMSETTER(ControllerType)(StorageControllerType_ICH6);
1649 else
1650 throw setError(VBOX_E_FILE_ERROR,
1651 tr("Invalid IDE controller type \"%s\""),
1652 pcszIDEType);
1653 if (FAILED(rc)) throw rc;
1654 }
1655#ifdef VBOX_WITH_AHCI
1656 /* Hard disk controller SATA */
1657 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSATA = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSATA);
1658 if (vsdeHDCSATA.size() > 1)
1659 throw setError(VBOX_E_FILE_ERROR,
1660 tr("Too many SATA controllers in OVF; import facility only supports one"));
1661 if (vsdeHDCSATA.size() > 0)
1662 {
1663 ComPtr<IStorageController> pController;
1664 const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVbox;
1665 if (hdcVBox == "AHCI")
1666 {
1667 rc = pNewMachine->AddStorageController(Bstr("SATA Controller"), StorageBus_SATA, pController.asOutParam());
1668 if (FAILED(rc)) throw rc;
1669 }
1670 else
1671 throw setError(VBOX_E_FILE_ERROR,
1672 tr("Invalid SATA controller type \"%s\""),
1673 hdcVBox.c_str());
1674 }
1675#endif /* VBOX_WITH_AHCI */
1676
1677#ifdef VBOX_WITH_LSILOGIC
1678 /* Hard disk controller SCSI */
1679 std::list<VirtualSystemDescriptionEntry*> vsdeHDCSCSI = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerSCSI);
1680 if (vsdeHDCSCSI.size() > 1)
1681 throw setError(VBOX_E_FILE_ERROR,
1682 tr("Too many SCSI controllers in OVF; import facility only supports one"));
1683 if (vsdeHDCSCSI.size() > 0)
1684 {
1685 ComPtr<IStorageController> pController;
1686 StorageControllerType_T controllerType;
1687 const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVbox;
1688 if (hdcVBox == "LsiLogic")
1689 controllerType = StorageControllerType_LsiLogic;
1690 else if (hdcVBox == "BusLogic")
1691 controllerType = StorageControllerType_BusLogic;
1692 else
1693 throw setError(VBOX_E_FILE_ERROR,
1694 tr("Invalid SCSI controller type \"%s\""),
1695 hdcVBox.c_str());
1696
1697 rc = pNewMachine->AddStorageController(Bstr("SCSI Controller"), StorageBus_SCSI, pController.asOutParam());
1698 if (FAILED(rc)) throw rc;
1699 rc = pController->COMSETTER(ControllerType)(controllerType);
1700 if (FAILED(rc)) throw rc;
1701 }
1702#endif /* VBOX_WITH_LSILOGIC */
1703
1704 /* Now its time to register the machine before we add any hard disks */
1705 rc = mVirtualBox->RegisterMachine(pNewMachine);
1706 if (FAILED(rc)) throw rc;
1707
1708 // store new machine for roll-back in case of errors
1709 Bstr bstrNewMachineId;
1710 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
1711 if (FAILED(rc)) throw rc;
1712 stack.llMachinesRegistered.push_back(bstrNewMachineId);
1713
1714 // Add floppies and CD-ROMs to the appropriate controllers.
1715 std::list<VirtualSystemDescriptionEntry*> vsdeFloppy = vsdescThis->findByType(VirtualSystemDescriptionType_Floppy);
1716 if (vsdeFloppy.size() > 1)
1717 throw setError(VBOX_E_FILE_ERROR,
1718 tr("Too many floppy controllers in OVF; import facility only supports one"));
1719 std::list<VirtualSystemDescriptionEntry*> vsdeCDROM = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM);
1720 if ( (vsdeFloppy.size() > 0)
1721 || (vsdeCDROM.size() > 0)
1722 )
1723 {
1724 // If there's an error here we need to close the session, so
1725 // we need another try/catch block.
1726
1727 try
1728 {
1729 // to attach things we need to open a session for the new machine
1730 rc = mVirtualBox->OpenSession(stack.pSession, bstrNewMachineId);
1731 if (FAILED(rc)) throw rc;
1732 stack.fSessionOpen = true;
1733
1734 ComPtr<IMachine> sMachine;
1735 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam());
1736 if (FAILED(rc)) throw rc;
1737
1738 // floppy first
1739 if (vsdeFloppy.size() == 1)
1740 {
1741 ComPtr<IStorageController> pController;
1742 rc = sMachine->AddStorageController(Bstr("Floppy Controller"), StorageBus_Floppy, pController.asOutParam());
1743 if (FAILED(rc)) throw rc;
1744
1745 Bstr bstrName;
1746 rc = pController->COMGETTER(Name)(bstrName.asOutParam());
1747 if (FAILED(rc)) throw rc;
1748
1749 // this is for rollback later
1750 MyHardDiskAttachment mhda;
1751 mhda.bstrUuid = bstrNewMachineId;
1752 mhda.pMachine = pNewMachine;
1753 mhda.controllerType = bstrName;
1754 mhda.lChannel = 0;
1755 mhda.lDevice = 0;
1756
1757 Log(("Attaching floppy\n"));
1758
1759 rc = sMachine->AttachDevice(mhda.controllerType,
1760 mhda.lChannel,
1761 mhda.lDevice,
1762 DeviceType_Floppy,
1763 NULL);
1764 if (FAILED(rc)) throw rc;
1765
1766 stack.llHardDiskAttachments.push_back(mhda);
1767 }
1768
1769 // CD-ROMs next
1770 for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin();
1771 jt != vsdeCDROM.end();
1772 ++jt)
1773 {
1774 // for now always attach to secondary master on IDE controller;
1775 // there seems to be no useful information in OVF where else to
1776 // attach it (@todo test with latest versions of OVF software)
1777
1778 // find the IDE controller
1779 const ovf::HardDiskController *pController = NULL;
1780 for (ovf::ControllersMap::const_iterator kt = vsysThis.mapControllers.begin();
1781 kt != vsysThis.mapControllers.end();
1782 ++kt)
1783 {
1784 if (kt->second.system == ovf::HardDiskController::IDE)
1785 {
1786 pController = &kt->second;
1787 break;
1788 }
1789 }
1790
1791 if (!pController)
1792 throw setError(VBOX_E_FILE_ERROR,
1793 tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox"));
1794
1795 // this is for rollback later
1796 MyHardDiskAttachment mhda;
1797 mhda.bstrUuid = bstrNewMachineId;
1798 mhda.pMachine = pNewMachine;
1799
1800 convertDiskAttachmentValues(*pController,
1801 2, // interpreted as secondary master
1802 mhda.controllerType, // Bstr
1803 mhda.lChannel,
1804 mhda.lDevice);
1805
1806 Log(("Attaching CD-ROM to channel %d on device %d\n", mhda.lChannel, mhda.lDevice));
1807
1808 rc = sMachine->AttachDevice(mhda.controllerType,
1809 mhda.lChannel,
1810 mhda.lDevice,
1811 DeviceType_DVD,
1812 NULL);
1813 if (FAILED(rc)) throw rc;
1814
1815 stack.llHardDiskAttachments.push_back(mhda);
1816 } // end for (itHD = avsdeHDs.begin();
1817
1818 rc = sMachine->SaveSettings();
1819 if (FAILED(rc)) throw rc;
1820
1821 // only now that we're done with all disks, close the session
1822 rc = stack.pSession->Close();
1823 if (FAILED(rc)) throw rc;
1824 stack.fSessionOpen = false;
1825 }
1826 catch(HRESULT /* aRC */)
1827 {
1828 if (stack.fSessionOpen)
1829 stack.pSession->Close();
1830
1831 throw;
1832 }
1833 }
1834
1835 // create the hard disks & connect them to the appropriate controllers
1836 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
1837 if (avsdeHDs.size() > 0)
1838 {
1839 // If there's an error here we need to close the session, so
1840 // we need another try/catch block.
1841 try
1842 {
1843 // to attach things we need to open a session for the new machine
1844 rc = mVirtualBox->OpenSession(stack.pSession, bstrNewMachineId);
1845 if (FAILED(rc)) throw rc;
1846 stack.fSessionOpen = true;
1847
1848 /* Iterate over all given disk images */
1849 list<VirtualSystemDescriptionEntry*>::const_iterator itHD;
1850 for (itHD = avsdeHDs.begin();
1851 itHD != avsdeHDs.end();
1852 ++itHD)
1853 {
1854 VirtualSystemDescriptionEntry *vsdeHD = *itHD;
1855
1856 // vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist
1857 // in the virtual system's disks map under that ID and also in the global images map
1858 ovf::VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef);
1859 // and find the disk from the OVF's disk list
1860 ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef);
1861 if ( (itVirtualDisk == vsysThis.mapVirtualDisks.end())
1862 || (itDiskImage == stack.mapDisks.end())
1863 )
1864 throw setError(E_FAIL,
1865 tr("Internal inconsistency looking up disk image '%s'"),
1866 vsdeHD->strRef.c_str());
1867
1868 const ovf::DiskImage &ovfDiskImage = itDiskImage->second;
1869 const ovf::VirtualDisk &ovfVdisk = itVirtualDisk->second;
1870
1871 ComPtr<IMedium> pTargetHD;
1872 importOneDiskImage(ovfDiskImage,
1873 vsdeHD->strVbox,
1874 pTargetHD,
1875 stack);
1876
1877 // now use the new uuid to attach the disk image to our new machine
1878 ComPtr<IMachine> sMachine;
1879 rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam());
1880 if (FAILED(rc)) throw rc;
1881 Bstr hdId;
1882 rc = pTargetHD->COMGETTER(Id)(hdId.asOutParam());
1883 if (FAILED(rc)) throw rc;
1884
1885 // find the hard disk controller to which we should attach
1886 ovf::HardDiskController hdc = (*vsysThis.mapControllers.find(ovfVdisk.idController)).second;
1887
1888 // this is for rollback later
1889 MyHardDiskAttachment mhda;
1890 mhda.bstrUuid = bstrNewMachineId;
1891 mhda.pMachine = pNewMachine;
1892
1893 convertDiskAttachmentValues(hdc,
1894 ovfVdisk.ulAddressOnParent,
1895 mhda.controllerType, // Bstr
1896 mhda.lChannel,
1897 mhda.lDevice);
1898
1899 Log(("Attaching disk %s to channel %d on device %d\n", vsdeHD->strVbox.c_str(), mhda.lChannel, mhda.lDevice));
1900
1901 rc = sMachine->AttachDevice(mhda.controllerType, // wstring name
1902 mhda.lChannel, // long controllerPort
1903 mhda.lDevice, // long device
1904 DeviceType_HardDisk, // DeviceType_T type
1905 hdId); // uuid id
1906 if (FAILED(rc)) throw rc;
1907
1908 stack.llHardDiskAttachments.push_back(mhda);
1909
1910 rc = sMachine->SaveSettings();
1911 if (FAILED(rc)) throw rc;
1912 } // end for (itHD = avsdeHDs.begin();
1913
1914 // only now that we're done with all disks, close the session
1915 rc = stack.pSession->Close();
1916 if (FAILED(rc)) throw rc;
1917 stack.fSessionOpen = false;
1918 }
1919 catch(HRESULT /* aRC */)
1920 {
1921 if (stack.fSessionOpen)
1922 stack.pSession->Close();
1923
1924 throw;
1925 }
1926 }
1927}
1928
1929/**
1930 * Imports one OVF virtual system (described by a vbox:Machine tag represented by the given config
1931 * structure) into VirtualBox by creating an IMachine instance, which is returned.
1932 *
1933 * This throws HRESULT error codes for anything that goes wrong, in which case the caller must clean
1934 * up any leftovers from this function. For this, the given ImportStack instance has received information
1935 * about what needs cleaning up (to support rollback).
1936 *
1937 * The machine config stored in the settings::MachineConfigFile structure contains the UUIDs of
1938 * the disk attachments used by the machine when it was exported. We also add vbox:uuid attributes
1939 * to the OVF disks sections so we can look them up. While importing these UUIDs into a second host
1940 * will most probably work, reimporting them into the same host will cause conflicts, so we always
1941 * generate new ones on import. This involves the following:
1942 *
1943 * 1) Scan the machine config for disk attachments.
1944 *
1945 * 2) For each disk attachment found, look up the OVF disk image from the disk references section
1946 * and import the disk into VirtualBox, which creates a new UUID for it. In the machine config,
1947 * replace the old UUID with the new one.
1948 *
1949 * 3) Create the VirtualBox machine with the modfified machine config.
1950 *
1951 * @param config
1952 * @param pNewMachine
1953 * @param stack
1954 */
1955void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThis,
1956 ComPtr<IMachine> &pReturnNewMachine,
1957 ImportStack &stack)
1958{
1959 Assert(vsdescThis->m->pConfig);
1960
1961 settings::MachineConfigFile &config = *vsdescThis->m->pConfig;
1962
1963 Utf8Str strDefaultHardDiskFolder;
1964 HRESULT rc = getDefaultHardDiskFolder(strDefaultHardDiskFolder);
1965 if (FAILED(rc)) throw rc;
1966
1967 // step 1): scan the machine config for attachments
1968 for (settings::StorageControllersList::iterator sit = config.storageMachine.llStorageControllers.begin();
1969 sit != config.storageMachine.llStorageControllers.end();
1970 ++sit)
1971 {
1972 settings::StorageController &sc = *sit;
1973
1974 for (settings::AttachedDevicesList::iterator dit = sc.llAttachedDevices.begin();
1975 dit != sc.llAttachedDevices.end();
1976 ++dit)
1977 {
1978 settings::AttachedDevice &d = *dit;
1979
1980 if (d.uuid.isEmpty())
1981 // empty DVD and floppy media
1982 continue;
1983
1984 // convert the Guid to string
1985 Utf8Str strUuid = d.uuid.toString();
1986
1987 // there must be an image in the OVF disk structs with the same UUID
1988 bool fFound = false;
1989 for (ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin();
1990 oit != stack.mapDisks.end();
1991 ++oit)
1992 {
1993 const ovf::DiskImage &di = oit->second;
1994
1995 if (di.uuidVbox == strUuid)
1996 {
1997 Utf8Str strTargetPath(strDefaultHardDiskFolder);
1998 strTargetPath.append(RTPATH_DELIMITER);
1999 strTargetPath.append(di.strHref);
2000 searchUniqueDiskImageFilePath(strTargetPath);
2001
2002 // step 2): for each attachment, import the disk...
2003 ComPtr<IMedium> pTargetHD;
2004 importOneDiskImage(di,
2005 strTargetPath,
2006 pTargetHD,
2007 stack);
2008
2009 // ... and replace the old UUID in the machine config with the one of
2010 // the imported disk that was just created
2011 Bstr hdId;
2012 rc = pTargetHD->COMGETTER(Id)(hdId.asOutParam());
2013 if (FAILED(rc)) throw rc;
2014
2015 d.uuid = hdId;
2016
2017 fFound = true;
2018 break;
2019 }
2020 }
2021
2022 // no disk with such a UUID found:
2023 if (!fFound)
2024 throw setError(E_FAIL,
2025 tr("<vbox:Machine> element in OVF contains a medium attachment for the disk image %s but the OVF describes no such image"),
2026 strUuid.raw());
2027 } // for (settings::AttachedDevicesList::const_iterator dit = sc.llAttachedDevices.begin();
2028 } // for (settings::StorageControllersList::const_iterator sit = config.storageMachine.llStorageControllers.begin();
2029
2030 // step 3): create the machine and have it import the config
2031
2032 // use the name that we computed in the OVF fields to avoid duplicates
2033 std::list<VirtualSystemDescriptionEntry*> vsdeName = vsdescThis->findByType(VirtualSystemDescriptionType_Name);
2034 if (vsdeName.size() < 1)
2035 throw setError(VBOX_E_FILE_ERROR,
2036 tr("Missing VM name"));
2037 const Utf8Str &strNameVBox = vsdeName.front()->strVbox;
2038
2039 ComObjPtr<Machine> pNewMachine;
2040 rc = pNewMachine.createObject();
2041 if (FAILED(rc)) throw rc;
2042
2043 // this magic constructor fills the new machine object with the MachineConfig
2044 // instance that we created from the vbox:Machine
2045 rc = pNewMachine->init(mVirtualBox,
2046 strNameVBox, // name from just above (can be suffixed to avoid duplicates)
2047 config); // the whole machine config
2048 if (FAILED(rc)) throw rc;
2049
2050 // return the new machine as an IMachine
2051 IMachine *p;
2052 rc = pNewMachine.queryInterfaceTo(&p);
2053 if (FAILED(rc)) throw rc;
2054 pReturnNewMachine = p;
2055
2056 // and register it
2057 rc = mVirtualBox->RegisterMachine(pNewMachine);
2058 if (FAILED(rc)) throw rc;
2059
2060 // store new machine for roll-back in case of errors
2061 Bstr bstrNewMachineId;
2062 rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam());
2063 if (FAILED(rc)) throw rc;
2064 stack.llMachinesRegistered.push_back(bstrNewMachineId);
2065}
2066
2067/**
2068 * Worker code for importing OVF from the cloud. This is called from Appliance::taskThreadImportOrExport()
2069 * in S3 mode and therefore runs on the OVF import worker thread. This then starts a second worker
2070 * thread to import from temporary files (see Appliance::importFS()).
2071 * @param pTask
2072 * @return
2073 */
2074HRESULT Appliance::importS3(TaskOVF *pTask)
2075{
2076 LogFlowFuncEnter();
2077 LogFlowFunc(("Appliance %p\n", this));
2078
2079 AutoCaller autoCaller(this);
2080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2081
2082 AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
2083
2084 int vrc = VINF_SUCCESS;
2085 RTS3 hS3 = NIL_RTS3;
2086 char szOSTmpDir[RTPATH_MAX];
2087 RTPathTemp(szOSTmpDir, sizeof(szOSTmpDir));
2088 /* The template for the temporary directory created below */
2089 char *pszTmpDir;
2090 RTStrAPrintf(&pszTmpDir, "%s"RTPATH_SLASH_STR"vbox-ovf-XXXXXX", szOSTmpDir);
2091 list< pair<Utf8Str, ULONG> > filesList;
2092
2093 HRESULT rc = S_OK;
2094 try
2095 {
2096 /* Extract the bucket */
2097 Utf8Str tmpPath = pTask->locInfo.strPath;
2098 Utf8Str bucket;
2099 parseBucket(tmpPath, bucket);
2100
2101 /* We need a temporary directory which we can put the all disk images
2102 * in */
2103 vrc = RTDirCreateTemp(pszTmpDir);
2104 if (RT_FAILURE(vrc))
2105 throw setError(VBOX_E_FILE_ERROR,
2106 tr("Cannot create temporary directory '%s'"), pszTmpDir);
2107
2108 /* Add every disks of every virtual system to an internal list */
2109 list< ComObjPtr<VirtualSystemDescription> >::const_iterator it;
2110 for (it = m->virtualSystemDescriptions.begin();
2111 it != m->virtualSystemDescriptions.end();
2112 ++it)
2113 {
2114 ComObjPtr<VirtualSystemDescription> vsdescThis = (*it);
2115 std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage);
2116 std::list<VirtualSystemDescriptionEntry*>::const_iterator itH;
2117 for (itH = avsdeHDs.begin();
2118 itH != avsdeHDs.end();
2119 ++itH)
2120 {
2121 const Utf8Str &strTargetFile = (*itH)->strOvf;
2122 if (!strTargetFile.isEmpty())
2123 {
2124 /* The temporary name of the target disk file */
2125 Utf8StrFmt strTmpDisk("%s/%s", pszTmpDir, RTPathFilename(strTargetFile.c_str()));
2126 filesList.push_back(pair<Utf8Str, ULONG>(strTmpDisk, (*itH)->ulSizeMB));
2127 }
2128 }
2129 }
2130
2131 /* Next we have to download the disk images */
2132 vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING);
2133 if (RT_FAILURE(vrc))
2134 throw setError(VBOX_E_IPRT_ERROR,
2135 tr("Cannot create S3 service handler"));
2136 RTS3SetProgressCallback(hS3, pTask->updateProgress, &pTask);
2137
2138 /* Download all files */
2139 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
2140 {
2141 const pair<Utf8Str, ULONG> &s = (*it1);
2142 const Utf8Str &strSrcFile = s.first;
2143 /* Construct the source file name */
2144 char *pszFilename = RTPathFilename(strSrcFile.c_str());
2145 /* Advance to the next operation */
2146 if (!pTask->pProgress.isNull())
2147 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), s.second);
2148
2149 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strSrcFile.c_str());
2150 if (RT_FAILURE(vrc))
2151 {
2152 if (vrc == VERR_S3_CANCELED)
2153 throw S_OK; /* todo: !!!!!!!!!!!!! */
2154 else if (vrc == VERR_S3_ACCESS_DENIED)
2155 throw setError(E_ACCESSDENIED,
2156 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
2157 else if (vrc == VERR_S3_NOT_FOUND)
2158 throw setError(VBOX_E_FILE_ERROR,
2159 tr("Cannot download file '%s' from S3 storage server (File not found)"), pszFilename);
2160 else
2161 throw setError(VBOX_E_IPRT_ERROR,
2162 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
2163 }
2164 }
2165
2166 /* Provide a OVF file (haven't to exist) so the import routine can
2167 * figure out where the disk images/manifest file are located. */
2168 Utf8StrFmt strTmpOvf("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str()));
2169 /* Now check if there is an manifest file. This is optional. */
2170 Utf8Str strManifestFile = manifestFileName(strTmpOvf);
2171 char *pszFilename = RTPathFilename(strManifestFile.c_str());
2172 if (!pTask->pProgress.isNull())
2173 pTask->pProgress->SetNextOperation(BstrFmt(tr("Downloading file '%s'"), pszFilename), 1);
2174
2175 /* Try to download it. If the error is VERR_S3_NOT_FOUND, it isn't fatal. */
2176 vrc = RTS3GetKey(hS3, bucket.c_str(), pszFilename, strManifestFile.c_str());
2177 if (RT_SUCCESS(vrc))
2178 filesList.push_back(pair<Utf8Str, ULONG>(strManifestFile, 0));
2179 else if (RT_FAILURE(vrc))
2180 {
2181 if (vrc == VERR_S3_CANCELED)
2182 throw S_OK; /* todo: !!!!!!!!!!!!! */
2183 else if (vrc == VERR_S3_NOT_FOUND)
2184 vrc = VINF_SUCCESS; /* Not found is ok */
2185 else if (vrc == VERR_S3_ACCESS_DENIED)
2186 throw setError(E_ACCESSDENIED,
2187 tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right. Also check that your host clock is properly synced"), pszFilename);
2188 else
2189 throw setError(VBOX_E_IPRT_ERROR,
2190 tr("Cannot download file '%s' from S3 storage server (%Rrc)"), pszFilename, vrc);
2191 }
2192
2193 /* Close the connection early */
2194 RTS3Destroy(hS3);
2195 hS3 = NIL_RTS3;
2196
2197 if (!pTask->pProgress.isNull())
2198 pTask->pProgress->SetNextOperation(BstrFmt(tr("Importing appliance")), m->ulWeightPerOperation);
2199
2200 ComObjPtr<Progress> progress;
2201 /* Import the whole temporary OVF & the disk images */
2202 LocationInfo li;
2203 li.strPath = strTmpOvf;
2204 rc = importImpl(li, progress);
2205 if (FAILED(rc)) throw rc;
2206
2207 /* Unlock the appliance for the fs import thread */
2208 appLock.release();
2209 /* Wait until the import is done, but report the progress back to the
2210 caller */
2211 ComPtr<IProgress> progressInt(progress);
2212 waitForAsyncProgress(pTask->pProgress, progressInt); /* Any errors will be thrown */
2213
2214 /* Again lock the appliance for the next steps */
2215 appLock.acquire();
2216 }
2217 catch(HRESULT aRC)
2218 {
2219 rc = aRC;
2220 }
2221 /* Cleanup */
2222 RTS3Destroy(hS3);
2223 /* Delete all files which where temporary created */
2224 for (list< pair<Utf8Str, ULONG> >::const_iterator it1 = filesList.begin(); it1 != filesList.end(); ++it1)
2225 {
2226 const char *pszFilePath = (*it1).first.c_str();
2227 if (RTPathExists(pszFilePath))
2228 {
2229 vrc = RTFileDelete(pszFilePath);
2230 if (RT_FAILURE(vrc))
2231 rc = setError(VBOX_E_FILE_ERROR,
2232 tr("Cannot delete file '%s' (%Rrc)"), pszFilePath, vrc);
2233 }
2234 }
2235 /* Delete the temporary directory */
2236 if (RTPathExists(pszTmpDir))
2237 {
2238 vrc = RTDirRemove(pszTmpDir);
2239 if (RT_FAILURE(vrc))
2240 rc = setError(VBOX_E_FILE_ERROR,
2241 tr("Cannot delete temporary directory '%s' (%Rrc)"), pszTmpDir, vrc);
2242 }
2243 if (pszTmpDir)
2244 RTStrFree(pszTmpDir);
2245
2246 LogFlowFunc(("rc=%Rhrc\n", rc));
2247 LogFlowFuncLeave();
2248
2249 return rc;
2250}
2251
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