VirtualBox

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

Last change on this file since 25275 was 25165, checked in by vboxsync, 14 years ago

VBoxManageImport.cpp: Iff the cpu count is supposed to be validated here, then use constants from VBox/param.h.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use