VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageImport.cpp@ 23285

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

OVF: Added remote OVF downloading.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.4 KB
Line 
1/* $Id: VBoxManageImport.cpp 21612 2009-07-15 15:00:34Z vboxsync $ */
2/** @file
3 * VBoxManage - The appliance-related commands.
4 */
5
6/*
7 * Copyright (C) 2009 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#ifndef VBOX_ONLY_DOCS
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#ifndef VBOX_ONLY_DOCS
28#include <VBox/com/com.h>
29#include <VBox/com/string.h>
30#include <VBox/com/Guid.h>
31#include <VBox/com/array.h>
32#include <VBox/com/ErrorInfo.h>
33#include <VBox/com/errorprint.h>
34#include <VBox/com/EventQueue.h>
35
36#include <VBox/com/VirtualBox.h>
37
38#include <list>
39#include <map>
40#endif /* !VBOX_ONLY_DOCS */
41
42#include <iprt/stream.h>
43#include <iprt/getopt.h>
44#include <iprt/ctype.h>
45#include <iprt/path.h>
46#include <iprt/file.h>
47
48#include <VBox/log.h>
49
50#include "VBoxManage.h"
51using namespace com;
52
53
54// funcs
55///////////////////////////////////////////////////////////////////////////////
56
57typedef std::map<Utf8Str, Utf8Str> ArgsMap; // pairs of strings like "vmname" => "newvmname"
58typedef std::map<uint32_t, ArgsMap> ArgsMapsMap; // map of maps, one for each virtual system, sorted by index
59
60typedef std::map<uint32_t, bool> IgnoresMap; // pairs of numeric description entry indices
61typedef std::map<uint32_t, IgnoresMap> IgnoresMapsMap; // map of maps, one for each virtual system, sorted by index
62
63static bool findArgValue(Utf8Str &strOut,
64 ArgsMap *pmapArgs,
65 const Utf8Str &strKey)
66{
67 if (pmapArgs)
68 {
69 ArgsMap::iterator it;
70 it = pmapArgs->find(strKey);
71 if (it != pmapArgs->end())
72 {
73 strOut = it->second;
74 pmapArgs->erase(it);
75 return true;
76 }
77 }
78
79 return false;
80}
81
82static const RTGETOPTDEF g_aImportApplianceOptions[] =
83{
84 { "--dry-run", 'n', RTGETOPT_REQ_NOTHING },
85 { "-dry-run", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
86 { "--dryrun", 'n', RTGETOPT_REQ_NOTHING },
87 { "-dryrun", 'n', RTGETOPT_REQ_NOTHING }, // deprecated
88 { "--detailed-progress", 'P', RTGETOPT_REQ_NOTHING },
89 { "-detailed-progress", 'P', RTGETOPT_REQ_NOTHING }, // deprecated
90 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
91 { "-vsys", 's', RTGETOPT_REQ_UINT32 }, // deprecated
92 { "--ostype", 'o', RTGETOPT_REQ_STRING },
93 { "-ostype", 'o', RTGETOPT_REQ_STRING }, // deprecated
94 { "--vmname", 'V', RTGETOPT_REQ_STRING },
95 { "-vmname", 'V', RTGETOPT_REQ_STRING }, // deprecated
96 { "--description", 'd', RTGETOPT_REQ_STRING },
97 { "--eula", 'L', RTGETOPT_REQ_STRING },
98 { "-eula", 'L', RTGETOPT_REQ_STRING }, // deprecated
99 { "--unit", 'u', RTGETOPT_REQ_UINT32 },
100 { "-unit", 'u', RTGETOPT_REQ_UINT32 }, // deprecated
101 { "--ignore", 'x', RTGETOPT_REQ_NOTHING },
102 { "-ignore", 'x', RTGETOPT_REQ_NOTHING }, // deprecated
103 { "--scsitype", 'T', RTGETOPT_REQ_UINT32 },
104 { "-scsitype", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
105 { "--type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
106 { "-type", 'T', RTGETOPT_REQ_UINT32 }, // deprecated
107};
108
109int handleImportAppliance(HandlerArg *a)
110{
111 HRESULT rc = S_OK;
112
113 Utf8Str strOvfFilename;
114 bool fExecute = true; // if true, then we actually do the import
115 uint32_t ulCurVsys = (uint32_t)-1;
116 uint32_t ulCurUnit = (uint32_t)-1;
117 // for each --vsys X command, maintain a map of command line items
118 // (we'll parse them later after interpreting the OVF, when we can
119 // actually check whether they make sense semantically)
120 ArgsMapsMap mapArgsMapsPerVsys;
121 IgnoresMapsMap mapIgnoresMapsPerVsys;
122
123 int c;
124 RTGETOPTUNION ValueUnion;
125 RTGETOPTSTATE GetState;
126 // start at 0 because main() has hacked both the argc and argv given to us
127 RTGetOptInit(&GetState, a->argc, a->argv, g_aImportApplianceOptions, RT_ELEMENTS(g_aImportApplianceOptions), 0, 0 /* fFlags */);
128 while ((c = RTGetOpt(&GetState, &ValueUnion)))
129 {
130 switch (c)
131 {
132 case 'n': // --dry-run
133 fExecute = false;
134 break;
135
136 case 'P': // --detailed-progress
137 g_fDetailedProgress = true;
138 break;
139
140 case 's': // --vsys
141 ulCurVsys = ValueUnion.u32;
142 ulCurUnit = (uint32_t)-1;
143 break;
144
145 case 'o': // --ostype
146 if (ulCurVsys == (uint32_t)-1)
147 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
148 mapArgsMapsPerVsys[ulCurVsys]["ostype"] = ValueUnion.psz;
149 break;
150
151 case 'V': // --vmname
152 if (ulCurVsys == (uint32_t)-1)
153 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
154 mapArgsMapsPerVsys[ulCurVsys]["vmname"] = ValueUnion.psz;
155 break;
156
157 case 'd': // --description
158 if (ulCurVsys == (uint32_t)-1)
159 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
160 mapArgsMapsPerVsys[ulCurVsys]["description"] = ValueUnion.psz;
161 break;
162
163 case 'L': // --eula
164 if (ulCurVsys == (uint32_t)-1)
165 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
166 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
167 break;
168
169 case 'm': // --memory
170 if (ulCurVsys == (uint32_t)-1)
171 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
172 mapArgsMapsPerVsys[ulCurVsys]["memory"] = ValueUnion.psz;
173 break;
174
175 case 'u': // --unit
176 ulCurUnit = ValueUnion.u32;
177 break;
178
179 case 'x': // --ignore
180 if (ulCurVsys == (uint32_t)-1)
181 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
182 if (ulCurUnit == (uint32_t)-1)
183 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
184 mapIgnoresMapsPerVsys[ulCurVsys][ulCurUnit] = true;
185 break;
186
187 case 'T': // --scsitype
188 if (ulCurVsys == (uint32_t)-1)
189 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
190 if (ulCurUnit == (uint32_t)-1)
191 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
192 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("scsitype%u", ulCurUnit)] = ValueUnion.psz;
193 break;
194
195 case 'C': // --controller
196 if (ulCurVsys == (uint32_t)-1)
197 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
198 if (ulCurUnit == (uint32_t)-1)
199 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding --unit argument.", GetState.pDef->pszLong);
200 mapArgsMapsPerVsys[ulCurVsys][Utf8StrFmt("controller%u", ulCurUnit)] = ValueUnion.psz;
201 break;
202
203 case VINF_GETOPT_NOT_OPTION:
204 if (strOvfFilename.isEmpty())
205 strOvfFilename = ValueUnion.psz;
206 else
207 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid parameter '%s'", ValueUnion.psz);
208 break;
209
210 default:
211 if (c > 0)
212 {
213 if (RT_C_IS_PRINT(c))
214 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option -%c", c);
215 else
216 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Invalid option case %i", c);
217 }
218 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
219 return errorSyntax(USAGE_IMPORTAPPLIANCE, "unknown option: %s\n", ValueUnion.psz);
220 else if (ValueUnion.pDef)
221 return errorSyntax(USAGE_IMPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
222 else
223 return errorSyntax(USAGE_IMPORTAPPLIANCE, "error: %Rrs", c);
224 }
225 }
226
227 if (strOvfFilename.isEmpty())
228 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Not enough arguments for \"import\" command.");
229
230 do
231 {
232 ComPtr<IAppliance> pAppliance;
233 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
234
235 char *pszAbsFilePath;
236 if (strOvfFilename.startsWith("S3://", iprt::MiniString::CaseInsensitive) ||
237 strOvfFilename.startsWith("SunCloud://", iprt::MiniString::CaseInsensitive) ||
238 strOvfFilename.startsWith("webdav://", iprt::MiniString::CaseInsensitive))
239 pszAbsFilePath = RTStrDup(strOvfFilename.c_str());
240 else
241 pszAbsFilePath = RTPathAbsDup(strOvfFilename.c_str());
242 ComPtr<IProgress> progressRead;
243 CHECK_ERROR_BREAK(pAppliance, Read(Bstr(pszAbsFilePath), progressRead.asOutParam()));
244 RTStrFree(pszAbsFilePath);
245
246 rc = showProgress(progressRead);
247
248 if (FAILED(rc))
249 {
250 com::ProgressErrorInfo info(progressRead);
251 com::GluePrintErrorInfo(info);
252 com::GluePrintErrorContext("ImportAppliance", __FILE__, __LINE__);
253 return 1;
254 }
255
256 Bstr path; /* fetch the path, there is stuff like username/password removed if any */
257 CHECK_ERROR_BREAK(pAppliance, COMGETTER(Path)(path.asOutParam()));
258 // call interpret(); this can yield both warnings and errors, so we need
259 // to tinker with the error info a bit
260 RTPrintf("Interpreting %ls...\n", path.raw());
261 rc = pAppliance->Interpret();
262 com::ErrorInfo info0(pAppliance);
263
264 com::SafeArray<BSTR> aWarnings;
265 if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
266 {
267 size_t cWarnings = aWarnings.size();
268 for (unsigned i = 0; i < cWarnings; ++i)
269 {
270 Bstr bstrWarning(aWarnings[i]);
271 RTPrintf("WARNING: %ls.\n", bstrWarning.raw());
272 }
273 }
274
275 if (FAILED(rc)) // during interpret, after printing warnings
276 {
277 com::GluePrintErrorInfo(info0);
278 com::GluePrintErrorContext("Interpret", __FILE__, __LINE__);
279 break;
280 }
281
282 RTPrintf("OK.\n");
283
284 // fetch all disks
285 com::SafeArray<BSTR> retDisks;
286 CHECK_ERROR_BREAK(pAppliance,
287 COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
288 if (retDisks.size() > 0)
289 {
290 RTPrintf("Disks:");
291 for (unsigned i = 0; i < retDisks.size(); i++)
292 RTPrintf(" %ls", retDisks[i]);
293 RTPrintf("\n");
294 }
295
296 // fetch virtual system descriptions
297 com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
298 CHECK_ERROR_BREAK(pAppliance,
299 COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
300
301 size_t cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
302
303 // match command line arguments with virtual system descriptions;
304 // this is only to sort out invalid indices at this time
305 ArgsMapsMap::const_iterator it;
306 for (it = mapArgsMapsPerVsys.begin();
307 it != mapArgsMapsPerVsys.end();
308 ++it)
309 {
310 uint32_t ulVsys = it->first;
311 if (ulVsys >= cVirtualSystemDescriptions)
312 return errorSyntax(USAGE_IMPORTAPPLIANCE,
313 "Invalid index %RI32 with -vsys option; the OVF contains only %zu virtual system(s).",
314 ulVsys, cVirtualSystemDescriptions);
315 }
316
317 uint32_t cLicensesInTheWay = 0;
318
319 // dump virtual system descriptions and match command-line arguments
320 if (cVirtualSystemDescriptions > 0)
321 {
322 for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
323 {
324 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
325 com::SafeArray<BSTR> aRefs;
326 com::SafeArray<BSTR> aOvfValues;
327 com::SafeArray<BSTR> aVboxValues;
328 com::SafeArray<BSTR> aExtraConfigValues;
329 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
330 GetDescription(ComSafeArrayAsOutParam(retTypes),
331 ComSafeArrayAsOutParam(aRefs),
332 ComSafeArrayAsOutParam(aOvfValues),
333 ComSafeArrayAsOutParam(aVboxValues),
334 ComSafeArrayAsOutParam(aExtraConfigValues)));
335
336 RTPrintf("Virtual system %u:\n", i);
337
338 // look up the corresponding command line options, if any
339 ArgsMap *pmapArgs = NULL;
340 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
341 if (itm != mapArgsMapsPerVsys.end())
342 pmapArgs = &itm->second;
343
344 // this collects the final values for setFinalValues()
345 com::SafeArray<BOOL> aEnabled(retTypes.size());
346 com::SafeArray<BSTR> aFinalValues(retTypes.size());
347
348 for (unsigned a = 0; a < retTypes.size(); ++a)
349 {
350 VirtualSystemDescriptionType_T t = retTypes[a];
351
352 Utf8Str strOverride;
353
354 Bstr bstrFinalValue = aVboxValues[a];
355
356 bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
357
358 aEnabled[a] = true;
359
360 switch (t)
361 {
362 case VirtualSystemDescriptionType_OS:
363 if (findArgValue(strOverride, pmapArgs, "ostype"))
364 {
365 bstrFinalValue = strOverride;
366 RTPrintf("%2u: OS type specified with --ostype: \"%ls\"\n",
367 a, bstrFinalValue.raw());
368 }
369 else
370 RTPrintf("%2u: Suggested OS type: \"%ls\""
371 "\n (change with \"--vsys %u --ostype <type>\"; use \"list ostypes\" to list all possible values)\n",
372 a, bstrFinalValue.raw(), i);
373 break;
374
375 case VirtualSystemDescriptionType_Name:
376 if (findArgValue(strOverride, pmapArgs, "vmname"))
377 {
378 bstrFinalValue = strOverride;
379 RTPrintf("%2u: VM name specified with --vmname: \"%ls\"\n",
380 a, bstrFinalValue.raw());
381 }
382 else
383 RTPrintf("%2u: Suggested VM name \"%ls\""
384 "\n (change with \"--vsys %u --vmname <name>\")\n",
385 a, bstrFinalValue.raw(), i);
386 break;
387
388 case VirtualSystemDescriptionType_Product:
389 RTPrintf("%2u: Product (ignored): %ls\n",
390 a, aVboxValues[a]);
391 break;
392
393 case VirtualSystemDescriptionType_ProductUrl:
394 RTPrintf("%2u: ProductUrl (ignored): %ls\n",
395 a, aVboxValues[a]);
396 break;
397
398 case VirtualSystemDescriptionType_Vendor:
399 RTPrintf("%2u: Vendor (ignored): %ls\n",
400 a, aVboxValues[a]);
401 break;
402
403 case VirtualSystemDescriptionType_VendorUrl:
404 RTPrintf("%2u: VendorUrl (ignored): %ls\n",
405 a, aVboxValues[a]);
406 break;
407
408 case VirtualSystemDescriptionType_Version:
409 RTPrintf("%2u: Version (ignored): %ls\n",
410 a, aVboxValues[a]);
411 break;
412
413 case VirtualSystemDescriptionType_Description:
414 if (findArgValue(strOverride, pmapArgs, "description"))
415 {
416 bstrFinalValue = strOverride;
417 RTPrintf("%2u: Description specified with --description: \"%ls\"\n",
418 a, bstrFinalValue.raw());
419 }
420 else
421 RTPrintf("%2u: Description \"%ls\""
422 "\n (change with \"--vsys %u --description <desc>\")\n",
423 a, bstrFinalValue.raw(), i);
424 break;
425
426 case VirtualSystemDescriptionType_License:
427 ++cLicensesInTheWay;
428 if (findArgValue(strOverride, pmapArgs, "eula"))
429 {
430 if (strOverride == "show")
431 {
432 RTPrintf("%2u: End-user license agreement"
433 "\n (accept with \"--vsys %u --eula accept\"):"
434 "\n\n%ls\n\n",
435 a, i, bstrFinalValue.raw());
436 }
437 else if (strOverride == "accept")
438 {
439 RTPrintf("%2u: End-user license agreement (accepted)\n",
440 a);
441 --cLicensesInTheWay;
442 }
443 else
444 return errorSyntax(USAGE_IMPORTAPPLIANCE,
445 "Argument to --eula must be either \"show\" or \"accept\".");
446 }
447 else
448 RTPrintf("%2u: End-user license agreement"
449 "\n (display with \"--vsys %u --eula show\";"
450 "\n accept with \"--vsys %u --eula accept\")\n",
451 a, i, i);
452 break;
453
454 case VirtualSystemDescriptionType_CPU:
455 RTPrintf("%2u: Number of CPUs (ignored): %ls\n",
456 a, aVboxValues[a]);
457 break;
458
459 case VirtualSystemDescriptionType_Memory:
460 {
461 if (findArgValue(strOverride, pmapArgs, "memory"))
462 {
463 uint32_t ulMemMB;
464 if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
465 {
466 bstrFinalValue = strOverride;
467 RTPrintf("%2u: Guest memory specified with --memory: %ls MB\n",
468 a, bstrFinalValue.raw());
469 }
470 else
471 return errorSyntax(USAGE_IMPORTAPPLIANCE,
472 "Argument to --memory option must be a non-negative number.");
473 }
474 else
475 RTPrintf("%2u: Guest memory: %ls MB\n (change with \"--vsys %u --memory <MB>\")\n",
476 a, bstrFinalValue.raw(), i);
477 }
478 break;
479
480 case VirtualSystemDescriptionType_HardDiskControllerIDE:
481 if (fIgnoreThis)
482 {
483 RTPrintf("%2u: IDE controller, type %ls -- disabled\n",
484 a,
485 aVboxValues[a]);
486 aEnabled[a] = false;
487 }
488 else
489 RTPrintf("%2u: IDE controller, type %ls"
490 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
491 a,
492 aVboxValues[a],
493 i, a);
494 break;
495
496 case VirtualSystemDescriptionType_HardDiskControllerSATA:
497 if (fIgnoreThis)
498 {
499 RTPrintf("%2u: SATA controller, type %ls -- disabled\n",
500 a,
501 aVboxValues[a]);
502 aEnabled[a] = false;
503 }
504 else
505 RTPrintf("%2u: SATA controller, type %ls"
506 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
507 a,
508 aVboxValues[a],
509 i, a);
510 break;
511
512 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
513 if (fIgnoreThis)
514 {
515 RTPrintf("%2u: SCSI controller, type %ls -- disabled\n",
516 a,
517 aVboxValues[a]);
518 aEnabled[a] = false;
519 }
520 else
521 {
522 Utf8StrFmt strTypeArg("scsitype%u", a);
523 if (findArgValue(strOverride, pmapArgs, strTypeArg))
524 {
525 bstrFinalValue = strOverride;
526 RTPrintf("%2u: SCSI controller, type set with --unit %u --scsitype: \"%ls\"\n",
527 a,
528 a,
529 bstrFinalValue.raw());
530 }
531 else
532 RTPrintf("%2u: SCSI controller, type %ls"
533 "\n (change with \"--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}\";"
534 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
535 a,
536 aVboxValues[a],
537 i, a, i, a);
538 }
539 break;
540
541 case VirtualSystemDescriptionType_HardDiskImage:
542 if (fIgnoreThis)
543 {
544 RTPrintf("%2u: Hard disk image: source image=%ls -- disabled\n",
545 a,
546 aOvfValues[a]);
547 aEnabled[a] = false;
548 }
549 else
550 {
551 Utf8StrFmt strTypeArg("controller%u", a);
552 if (findArgValue(strOverride, pmapArgs, strTypeArg))
553 {
554 // strOverride now has the controller index as a number, but we
555 // need a "controller=X" format string
556 strOverride = Utf8StrFmt("controller=%s", strOverride.c_str());
557 Bstr bstrExtraConfigValue = strOverride;
558 bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
559 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
560 a,
561 aOvfValues[a],
562 aVboxValues[a],
563 aExtraConfigValues[a]);
564 }
565 else
566 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls"
567 "\n (change controller with \"--vsys %u --unit %u --controller <id>\";"
568 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
569 a,
570 aOvfValues[a],
571 aVboxValues[a],
572 aExtraConfigValues[a],
573 i, a, i, a);
574 }
575 break;
576
577 case VirtualSystemDescriptionType_CDROM:
578 if (fIgnoreThis)
579 {
580 RTPrintf("%2u: CD-ROM -- disabled\n",
581 a);
582 aEnabled[a] = false;
583 }
584 else
585 RTPrintf("%2u: CD-ROM"
586 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
587 a, i, a);
588 break;
589
590 case VirtualSystemDescriptionType_Floppy:
591 if (fIgnoreThis)
592 {
593 RTPrintf("%2u: Floppy -- disabled\n",
594 a);
595 aEnabled[a] = false;
596 }
597 else
598 RTPrintf("%2u: Floppy"
599 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
600 a, i, a);
601 break;
602
603 case VirtualSystemDescriptionType_NetworkAdapter:
604 RTPrintf("%2u: Network adapter: orig %ls, config %ls, extra %ls\n", // @todo implement once we have a plan for the back-end
605 a,
606 aOvfValues[a],
607 aVboxValues[a],
608 aExtraConfigValues[a]);
609 break;
610
611 case VirtualSystemDescriptionType_USBController:
612 if (fIgnoreThis)
613 {
614 RTPrintf("%2u: USB controller -- disabled\n",
615 a);
616 aEnabled[a] = false;
617 }
618 else
619 RTPrintf("%2u: USB controller"
620 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
621 a, i, a);
622 break;
623
624 case VirtualSystemDescriptionType_SoundCard:
625 if (fIgnoreThis)
626 {
627 RTPrintf("%2u: Sound card \"%ls\" -- disabled\n",
628 a,
629 aOvfValues[a]);
630 aEnabled[a] = false;
631 }
632 else
633 RTPrintf("%2u: Sound card (appliance expects \"%ls\", can change on import)"
634 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
635 a,
636 aOvfValues[a],
637 i,
638 a);
639 break;
640 }
641
642 bstrFinalValue.detachTo(&aFinalValues[a]);
643 }
644
645 if (fExecute)
646 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
647 SetFinalValues(ComSafeArrayAsInParam(aEnabled),
648 ComSafeArrayAsInParam(aFinalValues),
649 ComSafeArrayAsInParam(aExtraConfigValues)));
650
651 } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
652
653 if (cLicensesInTheWay == 1)
654 RTPrintf("ERROR: Cannot import until the license agreement listed above is accepted.\n");
655 else if (cLicensesInTheWay > 1)
656 RTPrintf("ERROR: Cannot import until the %c license agreements listed above are accepted.\n", cLicensesInTheWay);
657
658 if (!cLicensesInTheWay && fExecute)
659 {
660 // go!
661 ComPtr<IProgress> progress;
662 CHECK_ERROR_BREAK(pAppliance,
663 ImportMachines(progress.asOutParam()));
664
665 rc = showProgress(progress);
666
667 if (FAILED(rc))
668 {
669 com::ProgressErrorInfo info(progress);
670 com::GluePrintErrorInfo(info);
671 com::GluePrintErrorContext("ImportAppliance", __FILE__, __LINE__);
672 return 1;
673 }
674 else
675 RTPrintf("Successfully imported the appliance.\n");
676 }
677 } // end if (aVirtualSystemDescriptions.size() > 0)
678 } while (0);
679
680 return SUCCEEDED(rc) ? 0 : 1;
681}
682
683static const RTGETOPTDEF g_aExportOptions[]
684 = {
685 { "--output", 'o', RTGETOPT_REQ_STRING },
686 { "--legacy09", 'l', RTGETOPT_REQ_NOTHING },
687 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
688 { "--product", 'p', RTGETOPT_REQ_STRING },
689 { "--producturl", 'P', RTGETOPT_REQ_STRING },
690 { "--vendor", 'd', RTGETOPT_REQ_STRING },
691 { "--vendorurl", 'D', RTGETOPT_REQ_STRING },
692 { "--version", 'v', RTGETOPT_REQ_STRING },
693 { "--eula", 'e', RTGETOPT_REQ_STRING },
694 { "--eulafile", 'E', RTGETOPT_REQ_STRING },
695 };
696
697int handleExportAppliance(HandlerArg *a)
698{
699 HRESULT rc = S_OK;
700
701 Utf8Str strOutputFile;
702 Utf8Str strOvfFormat("ovf-1.0"); // the default export version
703 std::list< ComPtr<IMachine> > llMachines;
704
705 uint32_t ulCurVsys = (uint32_t)-1;
706 // for each --vsys X command, maintain a map of command line items
707 ArgsMapsMap mapArgsMapsPerVsys;
708 do
709 {
710 int c;
711
712 RTGETOPTUNION ValueUnion;
713 RTGETOPTSTATE GetState;
714 // start at 0 because main() has hacked both the argc and argv given to us
715 RTGetOptInit(&GetState, a->argc, a->argv, g_aExportOptions,
716 RT_ELEMENTS(g_aExportOptions), 0, 0 /* fFlags */);
717
718 Utf8Str strProductUrl;
719 while ((c = RTGetOpt(&GetState, &ValueUnion)))
720 {
721 switch (c)
722 {
723 case 'o': // --output
724 if (strOutputFile.length())
725 return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
726 else
727 strOutputFile = ValueUnion.psz;
728 break;
729
730 case 'l': // --legacy09
731 strOvfFormat = "ovf-0.9";
732 break;
733
734 case 's': // --vsys
735 ulCurVsys = ValueUnion.u32;
736 break;
737
738 case 'p': // --product
739 if (ulCurVsys == (uint32_t)-1)
740 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
741 mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz;
742 break;
743
744 case 'P': // --producturl
745 if (ulCurVsys == (uint32_t)-1)
746 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
747 mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz;
748 break;
749
750 case 'd': // --vendor
751 if (ulCurVsys == (uint32_t)-1)
752 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
753 mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz;
754 break;
755
756 case 'D': // --vendorurl
757 if (ulCurVsys == (uint32_t)-1)
758 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
759 mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz;
760 break;
761
762 case 'v': // --version
763 if (ulCurVsys == (uint32_t)-1)
764 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
765 mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz;
766 break;
767
768 case 'e': // --eula
769 if (ulCurVsys == (uint32_t)-1)
770 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
771 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
772 break;
773
774 case 'E': // --eulafile
775 if (ulCurVsys == (uint32_t)-1)
776 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
777 mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz;
778 break;
779
780 case VINF_GETOPT_NOT_OPTION:
781 {
782 Utf8Str strMachine(ValueUnion.psz);
783 // must be machine: try UUID or name
784 ComPtr<IMachine> machine;
785 /* assume it's a UUID */
786 rc = a->virtualBox->GetMachine(Bstr(strMachine), machine.asOutParam());
787 if (FAILED(rc) || !machine)
788 {
789 /* must be a name */
790 CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine), machine.asOutParam()));
791 }
792
793 if (machine)
794 llMachines.push_back(machine);
795 }
796 break;
797
798 default:
799 if (c > 0)
800 {
801 if (RT_C_IS_GRAPH(c))
802 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: -%c", c);
803 else
804 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: %i", c);
805 }
806 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
807 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unknown option: %s", ValueUnion.psz);
808 else if (ValueUnion.pDef)
809 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
810 else
811 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%Rrs", c);
812 }
813
814 if (FAILED(rc))
815 break;
816 }
817
818 if (FAILED(rc))
819 break;
820
821 if (llMachines.size() == 0)
822 return errorSyntax(USAGE_EXPORTAPPLIANCE, "At least one machine must be specified with the export command.");
823 if (!strOutputFile.length())
824 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Missing --output argument with export command.");
825
826 // match command line arguments with the machines count
827 // this is only to sort out invalid indices at this time
828 ArgsMapsMap::const_iterator it;
829 for (it = mapArgsMapsPerVsys.begin();
830 it != mapArgsMapsPerVsys.end();
831 ++it)
832 {
833 uint32_t ulVsys = it->first;
834 if (ulVsys >= llMachines.size())
835 return errorSyntax(USAGE_EXPORTAPPLIANCE,
836 "Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s).",
837 ulVsys, llMachines.size());
838 }
839
840 ComPtr<IAppliance> pAppliance;
841 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
842
843 std::list< ComPtr<IMachine> >::iterator itM;
844 uint32_t i=0;
845 for (itM = llMachines.begin();
846 itM != llMachines.end();
847 ++itM, ++i)
848 {
849 ComPtr<IMachine> pMachine = *itM;
850 ComPtr<IVirtualSystemDescription> pVSD;
851 CHECK_ERROR_BREAK(pMachine, Export(pAppliance, pVSD.asOutParam()));
852 // Add additional info to the virtal system description if the user wants so
853 ArgsMap *pmapArgs = NULL;
854 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
855 if (itm != mapArgsMapsPerVsys.end())
856 pmapArgs = &itm->second;
857 if (pmapArgs)
858 {
859 ArgsMap::iterator itD;
860 for (itD = pmapArgs->begin();
861 itD != pmapArgs->end();
862 ++itD)
863 {
864 if (itD->first == "product")
865 pVSD->AddDescription (VirtualSystemDescriptionType_Product, Bstr(itD->second), Bstr(itD->second));
866 else if (itD->first == "producturl")
867 pVSD->AddDescription (VirtualSystemDescriptionType_ProductUrl, Bstr(itD->second), Bstr(itD->second));
868 else if (itD->first == "vendor")
869 pVSD->AddDescription (VirtualSystemDescriptionType_Vendor, Bstr(itD->second), Bstr(itD->second));
870 else if (itD->first == "vendorurl")
871 pVSD->AddDescription (VirtualSystemDescriptionType_VendorUrl, Bstr(itD->second), Bstr(itD->second));
872 else if (itD->first == "version")
873 pVSD->AddDescription (VirtualSystemDescriptionType_Version, Bstr(itD->second), Bstr(itD->second));
874 else if (itD->first == "eula")
875 pVSD->AddDescription (VirtualSystemDescriptionType_License, Bstr(itD->second), Bstr(itD->second));
876 else if (itD->first == "eulafile")
877 {
878 Utf8Str strContent;
879 void *pvFile;
880 size_t cbFile;
881 int rc = RTFileReadAll(itD->second.c_str(), &pvFile, &cbFile);
882 if (RT_SUCCESS(rc))
883 {
884 Bstr strContent((char*)pvFile);
885 pVSD->AddDescription (VirtualSystemDescriptionType_License, strContent, strContent);
886 RTFileReadAllFree(pvFile, cbFile);
887 }
888 else
889 {
890 RTPrintf("ERROR: Cannot read license file \"%s\" which should be included in the virtual system %u.\n",
891 itD->second.c_str(),
892 i);
893 return 1;
894 }
895 }
896 }
897 }
898 }
899
900 if (FAILED(rc))
901 break;
902
903 ComPtr<IProgress> progress;
904 char *pszAbsFilePath;
905 if (strOutputFile.startsWith("S3://", iprt::MiniString::CaseInsensitive) ||
906 strOutputFile.startsWith("SunCloud://", iprt::MiniString::CaseInsensitive) ||
907 strOutputFile.startsWith("webdav://", iprt::MiniString::CaseInsensitive))
908 pszAbsFilePath = RTStrDup(strOutputFile.c_str());
909 else
910 pszAbsFilePath = RTPathAbsDup(strOutputFile.c_str());
911 CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOvfFormat), Bstr(pszAbsFilePath), progress.asOutParam()));
912 RTStrFree(pszAbsFilePath);
913
914 rc = showProgress(progress);
915
916 if (FAILED(rc))
917 {
918 com::ProgressErrorInfo info(progress);
919 com::GluePrintErrorInfo(info);
920 com::GluePrintErrorContext("Write", __FILE__, __LINE__);
921 return 1;
922 }
923 else
924 RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
925
926 } while (0);
927
928 return SUCCEEDED(rc) ? 0 : 1;
929}
930
931#endif /* !VBOX_ONLY_DOCS */
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