VirtualBox

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

Last change on this file since 40257 was 38525, checked in by vboxsync, 13 years ago

FE/CLI: use CHECK_PROGRESS_ERROR

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