VirtualBox

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

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

OVF: fix SAS controller VBoxManage quirk

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