VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.9 KB
Line 
1/* $Id: VBoxManageImport.cpp 28800 2010-04-27 08:22:32Z 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_HardDiskControllerSCSI:
538 if (fIgnoreThis)
539 {
540 RTPrintf("%2u: SCSI controller, type %ls -- disabled\n",
541 a,
542 aVboxValues[a]);
543 aEnabled[a] = false;
544 }
545 else
546 {
547 Utf8StrFmt strTypeArg("scsitype%u", a);
548 if (findArgValue(strOverride, pmapArgs, strTypeArg))
549 {
550 bstrFinalValue = strOverride;
551 RTPrintf("%2u: SCSI controller, type set with --unit %u --scsitype: \"%ls\"\n",
552 a,
553 a,
554 bstrFinalValue.raw());
555 }
556 else
557 RTPrintf("%2u: SCSI controller, type %ls"
558 "\n (change with \"--vsys %u --unit %u --scsitype {BusLogic|LsiLogic}\";"
559 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
560 a,
561 aVboxValues[a],
562 i, a, i, a);
563 }
564 break;
565
566 case VirtualSystemDescriptionType_HardDiskImage:
567 if (fIgnoreThis)
568 {
569 RTPrintf("%2u: Hard disk image: source image=%ls -- disabled\n",
570 a,
571 aOvfValues[a]);
572 aEnabled[a] = false;
573 }
574 else
575 {
576 Utf8StrFmt strTypeArg("controller%u", a);
577 if (findArgValue(strOverride, pmapArgs, strTypeArg))
578 {
579 // strOverride now has the controller index as a number, but we
580 // need a "controller=X" format string
581 strOverride = Utf8StrFmt("controller=%s", strOverride.c_str());
582 Bstr bstrExtraConfigValue = strOverride;
583 bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
584 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls\n",
585 a,
586 aOvfValues[a],
587 aVboxValues[a],
588 aExtraConfigValues[a]);
589 }
590 else
591 RTPrintf("%2u: Hard disk image: source image=%ls, target path=%ls, %ls"
592 "\n (change controller with \"--vsys %u --unit %u --controller <id>\";"
593 "\n disable with \"--vsys %u --unit %u --ignore\")\n",
594 a,
595 aOvfValues[a],
596 aVboxValues[a],
597 aExtraConfigValues[a],
598 i, a, i, a);
599 }
600 break;
601
602 case VirtualSystemDescriptionType_CDROM:
603 if (fIgnoreThis)
604 {
605 RTPrintf("%2u: CD-ROM -- disabled\n",
606 a);
607 aEnabled[a] = false;
608 }
609 else
610 RTPrintf("%2u: CD-ROM"
611 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
612 a, i, a);
613 break;
614
615 case VirtualSystemDescriptionType_Floppy:
616 if (fIgnoreThis)
617 {
618 RTPrintf("%2u: Floppy -- disabled\n",
619 a);
620 aEnabled[a] = false;
621 }
622 else
623 RTPrintf("%2u: Floppy"
624 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
625 a, i, a);
626 break;
627
628 case VirtualSystemDescriptionType_NetworkAdapter:
629 RTPrintf("%2u: Network adapter: orig %ls, config %ls, extra %ls\n", // @todo implement once we have a plan for the back-end
630 a,
631 aOvfValues[a],
632 aVboxValues[a],
633 aExtraConfigValues[a]);
634 break;
635
636 case VirtualSystemDescriptionType_USBController:
637 if (fIgnoreThis)
638 {
639 RTPrintf("%2u: USB controller -- disabled\n",
640 a);
641 aEnabled[a] = false;
642 }
643 else
644 RTPrintf("%2u: USB controller"
645 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
646 a, i, a);
647 break;
648
649 case VirtualSystemDescriptionType_SoundCard:
650 if (fIgnoreThis)
651 {
652 RTPrintf("%2u: Sound card \"%ls\" -- disabled\n",
653 a,
654 aOvfValues[a]);
655 aEnabled[a] = false;
656 }
657 else
658 RTPrintf("%2u: Sound card (appliance expects \"%ls\", can change on import)"
659 "\n (disable with \"--vsys %u --unit %u --ignore\")\n",
660 a,
661 aOvfValues[a],
662 i,
663 a);
664 break;
665 }
666
667 bstrFinalValue.detachTo(&aFinalValues[a]);
668 }
669
670 if (fExecute)
671 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
672 SetFinalValues(ComSafeArrayAsInParam(aEnabled),
673 ComSafeArrayAsInParam(aFinalValues),
674 ComSafeArrayAsInParam(aExtraConfigValues)));
675
676 } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
677
678 if (cLicensesInTheWay == 1)
679 RTPrintf("ERROR: Cannot import until the license agreement listed above is accepted.\n");
680 else if (cLicensesInTheWay > 1)
681 RTPrintf("ERROR: Cannot import until the %c license agreements listed above are accepted.\n", cLicensesInTheWay);
682
683 if (!cLicensesInTheWay && fExecute)
684 {
685 // go!
686 ComPtr<IProgress> progress;
687 CHECK_ERROR_BREAK(pAppliance,
688 ImportMachines(progress.asOutParam()));
689
690 rc = showProgress(progress);
691
692 if (FAILED(rc))
693 {
694 com::ProgressErrorInfo info(progress);
695 com::GluePrintErrorInfo(info);
696 com::GluePrintErrorContext("ImportAppliance", __FILE__, __LINE__);
697 return 1;
698 }
699 else
700 RTPrintf("Successfully imported the appliance.\n");
701 }
702 } // end if (aVirtualSystemDescriptions.size() > 0)
703 } while (0);
704
705 return SUCCEEDED(rc) ? 0 : 1;
706}
707
708static const RTGETOPTDEF g_aExportOptions[]
709 = {
710 { "--output", 'o', RTGETOPT_REQ_STRING },
711 { "--legacy09", 'l', RTGETOPT_REQ_NOTHING },
712 { "--vsys", 's', RTGETOPT_REQ_UINT32 },
713 { "--product", 'p', RTGETOPT_REQ_STRING },
714 { "--producturl", 'P', RTGETOPT_REQ_STRING },
715 { "--vendor", 'd', RTGETOPT_REQ_STRING },
716 { "--vendorurl", 'D', RTGETOPT_REQ_STRING },
717 { "--version", 'v', RTGETOPT_REQ_STRING },
718 { "--eula", 'e', RTGETOPT_REQ_STRING },
719 { "--eulafile", 'E', RTGETOPT_REQ_STRING },
720 };
721
722int handleExportAppliance(HandlerArg *a)
723{
724 HRESULT rc = S_OK;
725
726 Utf8Str strOutputFile;
727 Utf8Str strOvfFormat("ovf-1.0"); // the default export version
728 std::list< ComPtr<IMachine> > llMachines;
729
730 uint32_t ulCurVsys = (uint32_t)-1;
731 // for each --vsys X command, maintain a map of command line items
732 ArgsMapsMap mapArgsMapsPerVsys;
733 do
734 {
735 int c;
736
737 RTGETOPTUNION ValueUnion;
738 RTGETOPTSTATE GetState;
739 // start at 0 because main() has hacked both the argc and argv given to us
740 RTGetOptInit(&GetState, a->argc, a->argv, g_aExportOptions,
741 RT_ELEMENTS(g_aExportOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
742
743 Utf8Str strProductUrl;
744 while ((c = RTGetOpt(&GetState, &ValueUnion)))
745 {
746 switch (c)
747 {
748 case 'o': // --output
749 if (strOutputFile.length())
750 return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
751 else
752 strOutputFile = ValueUnion.psz;
753 break;
754
755 case 'l': // --legacy09
756 strOvfFormat = "ovf-0.9";
757 break;
758
759 case 's': // --vsys
760 ulCurVsys = ValueUnion.u32;
761 break;
762
763 case 'p': // --product
764 if (ulCurVsys == (uint32_t)-1)
765 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
766 mapArgsMapsPerVsys[ulCurVsys]["product"] = ValueUnion.psz;
767 break;
768
769 case 'P': // --producturl
770 if (ulCurVsys == (uint32_t)-1)
771 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
772 mapArgsMapsPerVsys[ulCurVsys]["producturl"] = ValueUnion.psz;
773 break;
774
775 case 'd': // --vendor
776 if (ulCurVsys == (uint32_t)-1)
777 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
778 mapArgsMapsPerVsys[ulCurVsys]["vendor"] = ValueUnion.psz;
779 break;
780
781 case 'D': // --vendorurl
782 if (ulCurVsys == (uint32_t)-1)
783 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
784 mapArgsMapsPerVsys[ulCurVsys]["vendorurl"] = ValueUnion.psz;
785 break;
786
787 case 'v': // --version
788 if (ulCurVsys == (uint32_t)-1)
789 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
790 mapArgsMapsPerVsys[ulCurVsys]["version"] = ValueUnion.psz;
791 break;
792
793 case 'e': // --eula
794 if (ulCurVsys == (uint32_t)-1)
795 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
796 mapArgsMapsPerVsys[ulCurVsys]["eula"] = ValueUnion.psz;
797 break;
798
799 case 'E': // --eulafile
800 if (ulCurVsys == (uint32_t)-1)
801 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Option \"%s\" requires preceding --vsys argument.", GetState.pDef->pszLong);
802 mapArgsMapsPerVsys[ulCurVsys]["eulafile"] = ValueUnion.psz;
803 break;
804
805 case VINF_GETOPT_NOT_OPTION:
806 {
807 Utf8Str strMachine(ValueUnion.psz);
808 // must be machine: try UUID or name
809 ComPtr<IMachine> machine;
810 /* assume it's a UUID */
811 rc = a->virtualBox->GetMachine(Bstr(strMachine), machine.asOutParam());
812 if (FAILED(rc) || !machine)
813 {
814 /* must be a name */
815 CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine), machine.asOutParam()));
816 }
817
818 if (machine)
819 llMachines.push_back(machine);
820 }
821 break;
822
823 default:
824 if (c > 0)
825 {
826 if (RT_C_IS_GRAPH(c))
827 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: -%c", c);
828 else
829 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: %i", c);
830 }
831 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
832 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unknown option: %s", ValueUnion.psz);
833 else if (ValueUnion.pDef)
834 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
835 else
836 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%Rrs", c);
837 }
838
839 if (FAILED(rc))
840 break;
841 }
842
843 if (FAILED(rc))
844 break;
845
846 if (llMachines.size() == 0)
847 return errorSyntax(USAGE_EXPORTAPPLIANCE, "At least one machine must be specified with the export command.");
848 if (!strOutputFile.length())
849 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Missing --output argument with export command.");
850
851 // match command line arguments with the machines count
852 // this is only to sort out invalid indices at this time
853 ArgsMapsMap::const_iterator it;
854 for (it = mapArgsMapsPerVsys.begin();
855 it != mapArgsMapsPerVsys.end();
856 ++it)
857 {
858 uint32_t ulVsys = it->first;
859 if (ulVsys >= llMachines.size())
860 return errorSyntax(USAGE_EXPORTAPPLIANCE,
861 "Invalid index %RI32 with -vsys option; you specified only %zu virtual system(s).",
862 ulVsys, llMachines.size());
863 }
864
865 ComPtr<IAppliance> pAppliance;
866 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
867
868 std::list< ComPtr<IMachine> >::iterator itM;
869 uint32_t i=0;
870 for (itM = llMachines.begin();
871 itM != llMachines.end();
872 ++itM, ++i)
873 {
874 ComPtr<IMachine> pMachine = *itM;
875 ComPtr<IVirtualSystemDescription> pVSD;
876 CHECK_ERROR_BREAK(pMachine, Export(pAppliance, pVSD.asOutParam()));
877 // Add additional info to the virtal system description if the user wants so
878 ArgsMap *pmapArgs = NULL;
879 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
880 if (itm != mapArgsMapsPerVsys.end())
881 pmapArgs = &itm->second;
882 if (pmapArgs)
883 {
884 ArgsMap::iterator itD;
885 for (itD = pmapArgs->begin();
886 itD != pmapArgs->end();
887 ++itD)
888 {
889 if (itD->first == "product")
890 pVSD->AddDescription (VirtualSystemDescriptionType_Product, Bstr(itD->second), Bstr(itD->second));
891 else if (itD->first == "producturl")
892 pVSD->AddDescription (VirtualSystemDescriptionType_ProductUrl, Bstr(itD->second), Bstr(itD->second));
893 else if (itD->first == "vendor")
894 pVSD->AddDescription (VirtualSystemDescriptionType_Vendor, Bstr(itD->second), Bstr(itD->second));
895 else if (itD->first == "vendorurl")
896 pVSD->AddDescription (VirtualSystemDescriptionType_VendorUrl, Bstr(itD->second), Bstr(itD->second));
897 else if (itD->first == "version")
898 pVSD->AddDescription (VirtualSystemDescriptionType_Version, Bstr(itD->second), Bstr(itD->second));
899 else if (itD->first == "eula")
900 pVSD->AddDescription (VirtualSystemDescriptionType_License, Bstr(itD->second), Bstr(itD->second));
901 else if (itD->first == "eulafile")
902 {
903 Utf8Str strContent;
904 void *pvFile;
905 size_t cbFile;
906 int irc = RTFileReadAll(itD->second.c_str(), &pvFile, &cbFile);
907 if (RT_SUCCESS(irc))
908 {
909 Bstr bstrContent((char*)pvFile);
910 pVSD->AddDescription(VirtualSystemDescriptionType_License, bstrContent, bstrContent);
911 RTFileReadAllFree(pvFile, cbFile);
912 }
913 else
914 {
915 RTPrintf("ERROR: Cannot read license file \"%s\" which should be included in the virtual system %u.\n",
916 itD->second.c_str(),
917 i);
918 return 1;
919 }
920 }
921 }
922 }
923 }
924
925 if (FAILED(rc))
926 break;
927
928 ComPtr<IProgress> progress;
929 char *pszAbsFilePath;
930 if (strOutputFile.startsWith("S3://", iprt::MiniString::CaseInsensitive) ||
931 strOutputFile.startsWith("SunCloud://", iprt::MiniString::CaseInsensitive) ||
932 strOutputFile.startsWith("webdav://", iprt::MiniString::CaseInsensitive))
933 pszAbsFilePath = RTStrDup(strOutputFile.c_str());
934 else
935 pszAbsFilePath = RTPathAbsDup(strOutputFile.c_str());
936 CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOvfFormat), Bstr(pszAbsFilePath), progress.asOutParam()));
937 RTStrFree(pszAbsFilePath);
938
939 rc = showProgress(progress);
940
941 if (FAILED(rc))
942 {
943 com::ProgressErrorInfo info(progress);
944 com::GluePrintErrorInfo(info);
945 com::GluePrintErrorContext("Write", __FILE__, __LINE__);
946 return 1;
947 }
948 else
949 RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
950
951 } while (0);
952
953 return SUCCEEDED(rc) ? 0 : 1;
954}
955
956#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