VirtualBox

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

Last change on this file since 48533 was 46658, checked in by vboxsync, 12 years ago

include VBox/com/EventQueue only if necessary

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