VirtualBox

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

Last change on this file since 18164 was 18108, checked in by vboxsync, 16 years ago

VBoxManage: clean up various places which use RTGetOpt, fix error handling to deal with changed RTGetOpt semantics, make the double-dash options the recommended ones, updated help

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.4 KB
Line 
1/* $Id: VBoxManageImport.cpp 18108 2009-03-20 11:04:44Z 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/errorprint2.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
46#include <VBox/log.h>
47
48#include "VBoxManage.h"
49using namespace com;
50
51
52// funcs
53///////////////////////////////////////////////////////////////////////////////
54
55typedef std::map<Utf8Str, Utf8Str> ArgsMap; // pairs of strings like "-vmname" => "newvmname"
56typedef std::map<uint32_t, ArgsMap> ArgsMapsMap; // map of maps, one for each virtual system, sorted by index
57
58typedef std::map<uint32_t, bool> IgnoresMap; // pairs of numeric description entry indices
59typedef std::map<uint32_t, IgnoresMap> IgnoresMapsMap; // map of maps, one for each virtual system, sorted by index
60
61static bool findArgValue(Utf8Str &strOut,
62 ArgsMap *pmapArgs,
63 const Utf8Str &strKey)
64{
65 if (pmapArgs)
66 {
67 ArgsMap::iterator it;
68 it = pmapArgs->find(strKey);
69 if (it != pmapArgs->end())
70 {
71 strOut = it->second;
72 pmapArgs->erase(it);
73 return true;
74 }
75 }
76
77 return false;
78}
79
80int handleImportAppliance(HandlerArg *a)
81{
82 HRESULT rc = S_OK;
83
84 Utf8Str strOvfFilename;
85 bool fExecute = true; // if true, then we actually do the import
86
87 uint32_t ulCurVsys = (uint32_t)-1;
88
89 // for each -vsys X command, maintain a map of command line items
90 // (we'll parse them later after interpreting the OVF, when we can
91 // actually check whether they make sense semantically)
92 ArgsMapsMap mapArgsMapsPerVsys;
93 IgnoresMapsMap mapIgnoresMapsPerVsys;
94
95 for (int i = 0;
96 i < a->argc;
97 ++i)
98 {
99 bool fIsIgnore = false;
100 Utf8Str strThisArg(a->argv[i]);
101 if ( (strThisArg == "--dry-run")
102 || (strThisArg == "-dry-run")
103 || (strThisArg == "-n")
104 )
105 fExecute = false;
106 else if (strThisArg == "-vsys")
107 {
108 if (++i < a->argc)
109 {
110 uint32_t ulVsys;
111 if (VINF_SUCCESS != (rc = Utf8Str(a->argv[i]).toInt(ulVsys))) // don't use SUCCESS() macro, fail even on warnings
112 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Argument to -vsys option must be a non-negative number.");
113
114 ulCurVsys = ulVsys;
115 }
116 else
117 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Missing argument to -vsys option.");
118 }
119 else if ( (strThisArg == "-ostype")
120 || (strThisArg == "-vmname")
121 || (strThisArg == "-memory")
122 || (fIsIgnore = (strThisArg == "-ignore"))
123 || (strThisArg.substr(0, 5) == "-type")
124 || (strThisArg.substr(0, 11) == "-controller")
125 )
126 {
127 if (ulCurVsys == (uint32_t)-1)
128 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Option \"%s\" requires preceding -vsys argument.", strThisArg.c_str());
129
130 if (++i < a->argc)
131 if (fIsIgnore)
132 {
133 uint32_t ulItem;
134 if (VINF_SUCCESS != Utf8Str(a->argv[i]).toInt(ulItem))
135 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Argument to -vsys option must be a non-negative number.");
136
137 mapIgnoresMapsPerVsys[ulCurVsys][ulItem] = true;
138 }
139 else
140 {
141 // store both this arg and the next one in the strings map for later parsing
142 mapArgsMapsPerVsys[ulCurVsys][strThisArg] = Utf8Str(a->argv[i]);
143 }
144 else
145 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Missing argument to \"%s\" option.", strThisArg.c_str());
146 }
147 else if (strThisArg[0] == '-')
148 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Unknown option \"%s\".", strThisArg.c_str());
149 else if (!strOvfFilename)
150 strOvfFilename = strThisArg;
151 else
152 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Too many arguments for \"import\" command.");
153 }
154
155 if (!strOvfFilename)
156 return errorSyntax(USAGE_IMPORTAPPLIANCE, "Not enough arguments for \"import\" command.");
157
158 do
159 {
160 Bstr bstrOvfFilename(strOvfFilename);
161 ComPtr<IAppliance> pAppliance;
162 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
163
164 CHECK_ERROR_BREAK(pAppliance, Read(bstrOvfFilename));
165
166 // call interpret(); this can yield both warnings and errors, so we need
167 // to tinker with the error info a bit
168 RTPrintf("Interpreting %s...\n", strOvfFilename.c_str());
169 rc = pAppliance->Interpret();
170 com::ErrorInfo info0(pAppliance);
171
172 com::SafeArray<BSTR> aWarnings;
173 if (SUCCEEDED(pAppliance->GetWarnings(ComSafeArrayAsOutParam(aWarnings))))
174 {
175 unsigned cWarnings = aWarnings.size();
176 for (unsigned i = 0; i < cWarnings; ++i)
177 {
178 Bstr bstrWarning(aWarnings[i]);
179 RTPrintf("WARNING: %ls.\n", bstrWarning.raw());
180 }
181 }
182
183 if (FAILED(rc)) // during interpret, after printing warnings
184 {
185 com::GluePrintErrorInfo(info0);
186 com::GluePrintErrorContext("Interpret", __FILE__, __LINE__);
187 break;
188 }
189
190 RTPrintf("OK.\n");
191
192 // fetch all disks
193 com::SafeArray<BSTR> retDisks;
194 CHECK_ERROR_BREAK(pAppliance,
195 COMGETTER(Disks)(ComSafeArrayAsOutParam(retDisks)));
196 if (retDisks.size() > 0)
197 {
198 RTPrintf("Disks:");
199 for (unsigned i = 0; i < retDisks.size(); i++)
200 RTPrintf(" %ls", retDisks[i]);
201 RTPrintf("\n");
202 }
203
204 // fetch virtual system descriptions
205 com::SafeIfaceArray<IVirtualSystemDescription> aVirtualSystemDescriptions;
206 CHECK_ERROR_BREAK(pAppliance,
207 COMGETTER(VirtualSystemDescriptions)(ComSafeArrayAsOutParam(aVirtualSystemDescriptions)));
208
209 uint32_t cVirtualSystemDescriptions = aVirtualSystemDescriptions.size();
210
211 // match command line arguments with virtual system descriptions;
212 // this is only to sort out invalid indices at this time
213 ArgsMapsMap::const_iterator it;
214 for (it = mapArgsMapsPerVsys.begin();
215 it != mapArgsMapsPerVsys.end();
216 ++it)
217 {
218 uint32_t ulVsys = it->first;
219 if (ulVsys >= cVirtualSystemDescriptions)
220 return errorSyntax(USAGE_IMPORTAPPLIANCE,
221 "Invalid index %RI32 with -vsys option; the OVF contains only %RI32 virtual system(s).",
222 ulVsys, cVirtualSystemDescriptions);
223 }
224
225 // dump virtual system descriptions and match command-line arguments
226 if (cVirtualSystemDescriptions > 0)
227 {
228 for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
229 {
230 com::SafeArray<VirtualSystemDescriptionType_T> retTypes;
231 com::SafeArray<BSTR> aRefs;
232 com::SafeArray<BSTR> aOvfValues;
233 com::SafeArray<BSTR> aVboxValues;
234 com::SafeArray<BSTR> aExtraConfigValues;
235 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
236 GetDescription(ComSafeArrayAsOutParam(retTypes),
237 ComSafeArrayAsOutParam(aRefs),
238 ComSafeArrayAsOutParam(aOvfValues),
239 ComSafeArrayAsOutParam(aVboxValues),
240 ComSafeArrayAsOutParam(aExtraConfigValues)));
241
242 RTPrintf("Virtual system %i:\n", i);
243
244 // look up the corresponding command line options, if any
245 ArgsMap *pmapArgs = NULL;
246 ArgsMapsMap::iterator itm = mapArgsMapsPerVsys.find(i);
247 if (itm != mapArgsMapsPerVsys.end())
248 pmapArgs = &itm->second;
249
250 // this collects the final values for setFinalValues()
251 com::SafeArray<BOOL> aEnabled(retTypes.size());
252 com::SafeArray<BSTR> aFinalValues(retTypes.size());
253
254 for (unsigned a = 0; a < retTypes.size(); ++a)
255 {
256 VirtualSystemDescriptionType_T t = retTypes[a];
257
258 Utf8Str strOverride;
259
260 Bstr bstrFinalValue = aVboxValues[a];
261
262 bool fIgnoreThis = mapIgnoresMapsPerVsys[i][a];
263
264 aEnabled[a] = true;
265
266 switch (t)
267 {
268 case VirtualSystemDescriptionType_Name:
269 if (findArgValue(strOverride, pmapArgs, "-vmname"))
270 {
271 bstrFinalValue = strOverride;
272 RTPrintf("%2d: VM name specified with -vmname: \"%ls\"\n",
273 a, bstrFinalValue.raw());
274 }
275 else
276 RTPrintf("%2d: Suggested VM name \"%ls\""
277 "\n (change with \"-vsys %d -vmname <name>\")\n",
278 a, bstrFinalValue.raw(), i);
279 break;
280
281 case VirtualSystemDescriptionType_OS:
282 if (findArgValue(strOverride, pmapArgs, "-ostype"))
283 {
284 bstrFinalValue = strOverride;
285 RTPrintf("%2d: OS type specified with -ostype: \"%ls\"\n",
286 a, bstrFinalValue.raw());
287 }
288 else
289 RTPrintf("%2d: Suggested OS type: \"%ls\""
290 "\n (change with \"-vsys %d -ostype <type>\"; use \"list ostypes\" to list all)\n",
291 a, bstrFinalValue.raw(), i);
292 break;
293
294 case VirtualSystemDescriptionType_CPU:
295 RTPrintf("%2d: Number of CPUs (ignored): %ls\n",
296 a, aVboxValues[a]);
297 break;
298
299 case VirtualSystemDescriptionType_Memory:
300 {
301 if (findArgValue(strOverride, pmapArgs, "-memory"))
302 {
303 uint32_t ulMemMB;
304 if (VINF_SUCCESS == strOverride.toInt(ulMemMB))
305 {
306 bstrFinalValue = strOverride;
307 RTPrintf("%2d: Guest memory specified with -memory: %ls MB\n",
308 a, bstrFinalValue.raw());
309 }
310 else
311 return errorSyntax(USAGE_IMPORTAPPLIANCE,
312 "Argument to -memory option must be a non-negative number.");
313 }
314 else
315 RTPrintf("%2d: Guest memory: %ls MB\n (change with \"-vsys %d -memory <MB>\")\n",
316 a, bstrFinalValue.raw(), i);
317 }
318 break;
319
320 case VirtualSystemDescriptionType_HardDiskControllerIDE:
321 if (fIgnoreThis)
322 {
323 RTPrintf("%2d: IDE controller, type %ls -- disabled\n",
324 a,
325 aVboxValues[a]);
326 aEnabled[a] = false;
327 }
328 else
329 RTPrintf("%2d: IDE controller, type %ls"
330 "\n (disable with \"-vsys %d -ignore %d\")\n",
331 a,
332 aVboxValues[a],
333 i, a);
334 break;
335
336 case VirtualSystemDescriptionType_HardDiskControllerSATA:
337 if (fIgnoreThis)
338 {
339 RTPrintf("%2d: SATA controller, type %ls -- disabled\n",
340 a,
341 aVboxValues[a]);
342 aEnabled[a] = false;
343 }
344 else
345 RTPrintf("%2d: SATA controller, type %ls"
346 "\n (disable with \"-vsys %d -ignore %d\")\n",
347 a,
348 aVboxValues[a],
349 i, a);
350 break;
351
352 case VirtualSystemDescriptionType_HardDiskControllerSCSI:
353 if (fIgnoreThis)
354 {
355 RTPrintf("%2d: SCSI controller, type %ls -- disabled\n",
356 a,
357 aVboxValues[a]);
358 aEnabled[a] = false;
359 }
360 else
361 {
362 Utf8StrFmt strTypeArg("-type%RI16", a);
363 if (findArgValue(strOverride, pmapArgs, strTypeArg))
364 {
365 bstrFinalValue = strOverride;
366 RTPrintf("%2d: SCSI controller, type set with -type%d: %ls\n",
367 a,
368 a,
369 bstrFinalValue.raw());
370 }
371 else
372 RTPrintf("%2d: SCSI controller, type %ls"
373 "\n (change with \"-vsys %d -type%d {BusLogic|LsiLogic}\";"
374 "\n disable with \"-vsys %d -ignore %d\")\n",
375 a,
376 aVboxValues[a],
377 i, a, i, a);
378 }
379 break;
380
381 case VirtualSystemDescriptionType_HardDiskImage:
382 if (fIgnoreThis)
383 {
384 RTPrintf("%2d: Hard disk image: source image=%ls -- disabled\n",
385 a,
386 aOvfValues[a]);
387 aEnabled[a] = false;
388 }
389 else
390 {
391 Utf8StrFmt strTypeArg("-controller%RI16", a);
392 if (findArgValue(strOverride, pmapArgs, strTypeArg))
393 {
394 // strOverride now has the controller index as a number, but we
395 // need a "controller=X" format string
396 strOverride = Utf8StrFmt("controller=%s", strOverride.c_str());
397 Bstr bstrExtraConfigValue = strOverride;
398 bstrExtraConfigValue.detachTo(&aExtraConfigValues[a]);
399 RTPrintf("%2d: Hard disk image: source image=%ls, target path=%ls, %ls\n",
400 a,
401 aOvfValues[a],
402 aVboxValues[a],
403 aExtraConfigValues[a]);
404 }
405 else
406 RTPrintf("%2d: Hard disk image: source image=%ls, target path=%ls, %ls"
407 "\n (change controller with \"-vsys %d -controller%d <id>\";"
408 "\n disable with \"-vsys %d -ignore %d\")\n",
409 a,
410 aOvfValues[a],
411 aVboxValues[a],
412 aExtraConfigValues[a],
413 i, a, i, a);
414 }
415 break;
416
417 case VirtualSystemDescriptionType_CDROM:
418 if (fIgnoreThis)
419 {
420 RTPrintf("%2d: CD-ROM -- disabled\n",
421 a);
422 aEnabled[a] = false;
423 }
424 else
425 RTPrintf("%2d: CD-ROM"
426 "\n (disable with \"-vsys %d -ignore %d\")\n",
427 a, i, a);
428 break;
429
430 case VirtualSystemDescriptionType_Floppy:
431 if (fIgnoreThis)
432 {
433 RTPrintf("%2d: Floppy -- disabled\n",
434 a);
435 aEnabled[a] = false;
436 }
437 else
438 RTPrintf("%2d: Floppy"
439 "\n (disable with \"-vsys %d -ignore %d\")\n",
440 a, i, a);
441 break;
442
443 case VirtualSystemDescriptionType_NetworkAdapter:
444 RTPrintf("%2d: Network adapter: orig %ls, config %ls, extra %ls\n", // @todo implement once we have a plan for the back-end
445 a,
446 aOvfValues[a],
447 aVboxValues[a],
448 aExtraConfigValues[a]);
449 break;
450
451 case VirtualSystemDescriptionType_USBController:
452 if (fIgnoreThis)
453 {
454 RTPrintf("%2d: USB controller -- disabled\n",
455 a);
456 aEnabled[a] = false;
457 }
458 else
459 RTPrintf("%2d: USB controller"
460 "\n (disable with \"-vsys %d -ignore %d\")\n",
461 a, i, a);
462 break;
463
464 case VirtualSystemDescriptionType_SoundCard:
465 if (fIgnoreThis)
466 {
467 RTPrintf("%2d: Sound card \"%ls\" -- disabled\n",
468 a,
469 aOvfValues[a]);
470 aEnabled[a] = false;
471 }
472 else
473 RTPrintf("%2d: Sound card (appliance expects \"%ls\", can change on import)"
474 "\n (disable with \"-vsys %d -ignore %d\")\n",
475 a,
476 aOvfValues[a],
477 i,
478 a);
479 break;
480 }
481
482 bstrFinalValue.detachTo(&aFinalValues[a]);
483 }
484
485 if (fExecute)
486 CHECK_ERROR_BREAK(aVirtualSystemDescriptions[i],
487 SetFinalValues(ComSafeArrayAsInParam(aEnabled),
488 ComSafeArrayAsInParam(aFinalValues),
489 ComSafeArrayAsInParam(aExtraConfigValues)));
490
491 } // for (unsigned i = 0; i < cVirtualSystemDescriptions; ++i)
492
493 if (fExecute)
494 {
495 // go!
496 ComPtr<IProgress> progress;
497 CHECK_ERROR_BREAK(pAppliance,
498 ImportMachines(progress.asOutParam()));
499
500 showProgress(progress);
501
502 if (SUCCEEDED(rc))
503 progress->COMGETTER(ResultCode)(&rc);
504
505 if (FAILED(rc))
506 {
507 com::ProgressErrorInfo info(progress);
508 com::GluePrintErrorInfo(info);
509 com::GluePrintErrorContext("ImportAppliance", __FILE__, __LINE__);
510 }
511 else
512 RTPrintf("Successfully imported the appliance.\n");
513 }
514 } // end if (aVirtualSystemDescriptions.size() > 0)
515 } while (0);
516
517 return SUCCEEDED(rc) ? 0 : 1;
518}
519
520static const RTGETOPTDEF g_aExportOptions[]
521 = {
522 { "--output", 'o', RTGETOPT_REQ_STRING },
523 };
524
525int handleExportAppliance(HandlerArg *a)
526{
527 HRESULT rc = S_OK;
528
529 Utf8Str strOutputFile;
530 std::list< ComPtr<IMachine> > llMachines;
531
532 do
533 {
534 int c;
535
536 RTGETOPTUNION ValueUnion;
537 RTGETOPTSTATE GetState;
538 // start at 0 because main() has hacked both the argc and argv given to us
539 RTGetOptInit(&GetState, a->argc, a->argv, g_aExportOptions,
540 RT_ELEMENTS(g_aExportOptions), 0, 0 /* fFlags */);
541 while ((c = RTGetOpt(&GetState, &ValueUnion)))
542 {
543 switch (c)
544 {
545 case 'o': // --output
546 if (strOutputFile.length())
547 return errorSyntax(USAGE_EXPORTAPPLIANCE, "You can only specify --output once.");
548 else
549 strOutputFile = ValueUnion.psz;
550 break;
551
552 case VINF_GETOPT_NOT_OPTION:
553 {
554 Utf8Str strMachine(ValueUnion.psz);
555 // must be machine: try UUID or name
556 ComPtr<IMachine> machine;
557 /* assume it's a UUID */
558 rc = a->virtualBox->GetMachine(Guid(strMachine), machine.asOutParam());
559 if (FAILED(rc) || !machine)
560 {
561 /* must be a name */
562 CHECK_ERROR_BREAK(a->virtualBox, FindMachine(Bstr(strMachine), machine.asOutParam()));
563 }
564
565 if (machine)
566 llMachines.push_back(machine);
567 }
568 break;
569
570 default:
571 if (c > 0)
572 {
573 if (RT_C_IS_GRAPH(c))
574 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: -%c", c);
575 else
576 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unhandled option: %i", c);
577 }
578 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
579 return errorSyntax(USAGE_EXPORTAPPLIANCE, "unknown option: %s", ValueUnion.psz);
580 else if (ValueUnion.pDef)
581 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
582 else
583 return errorSyntax(USAGE_EXPORTAPPLIANCE, "%Rrs", c);
584 }
585
586 if (FAILED(rc))
587 break;
588 }
589
590 if (FAILED(rc))
591 break;
592
593 if (llMachines.size() == 0)
594 return errorSyntax(USAGE_EXPORTAPPLIANCE, "At least one machine must be specified with the export command.");
595 if (!strOutputFile.length())
596 return errorSyntax(USAGE_EXPORTAPPLIANCE, "Missing --output argument with export command.");
597
598 ComPtr<IAppliance> pAppliance;
599 CHECK_ERROR_BREAK(a->virtualBox, CreateAppliance(pAppliance.asOutParam()));
600
601 std::list< ComPtr<IMachine> >::iterator itM;
602 for (itM = llMachines.begin();
603 itM != llMachines.end();
604 ++itM)
605 {
606 ComPtr<IMachine> pMachine = *itM;
607 CHECK_ERROR_BREAK(pMachine, Export(pAppliance));
608 }
609
610 if (FAILED(rc))
611 break;
612
613 ComPtr<IProgress> progress;
614 CHECK_ERROR_BREAK(pAppliance, Write(Bstr(strOutputFile), progress.asOutParam()));
615
616 showProgress(progress);
617
618 if (SUCCEEDED(rc))
619 progress->COMGETTER(ResultCode)(&rc);
620
621 if (FAILED(rc))
622 {
623 com::ProgressErrorInfo info(progress);
624 com::GluePrintErrorInfo(info);
625 com::GluePrintErrorContext("Write", __FILE__, __LINE__);
626 }
627 else
628 RTPrintf("Successfully exported %d machine(s).\n", llMachines.size());
629
630 } while (0);
631
632 return SUCCEEDED(rc) ? 0 : 1;
633}
634
635#endif /* !VBOX_ONLY_DOCS */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette