VirtualBox

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

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

*: RTGetOpt cleanup related to --help and --version (now standard option). Use RTGetOptPrintError.

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