VirtualBox

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

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

VBoxManage: fix missing --memory option which was never parsed for some reason; add --cpu option since we do support multiple cpus now

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