VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageAppliance.cpp@ 35273

Last change on this file since 35273 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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

© 2023 Oracle
ContactPrivacy policyTerms of Use