VirtualBox

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

Last change on this file since 17185 was 17098, checked in by vboxsync, 16 years ago

VBoxManage: new export command line parsing

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