|
15 | 15 |
|
16 | 16 | import argparse
|
17 | 17 | import hashlib
|
| 18 | +import json |
18 | 19 | import os
|
19 | 20 | import re
|
20 | 21 | import sys
|
21 | 22 | import textwrap
|
22 | 23 | from datetime import datetime
|
23 | 24 |
|
| 25 | +import jsonschema |
24 | 26 | from vboxcommon import *
|
25 | 27 |
|
26 |
| - |
27 | 28 | DESCRIPTION = "Export one or more snapshots in the same VirtualBox VM as .ova, changing the network to a single Host-Only interface. Generate a file with the SHA256 of the exported OVA(s)."
|
28 | 29 |
|
29 | 30 | EPILOG = textwrap.dedent(
|
30 | 31 | """
|
31 | 32 | Example usage:
|
32 |
| - # Export the "FLARE-VM" snapshot from the "FLARE-VM.testing" VM |
33 |
| - vbox-export-snapshots.py "FLARE-VM.testing" --snapshot "FLARE-VM,.dynamic,Windows 10 VM with FLARE-VM default configuration" |
34 |
| -
|
35 |
| - # Export three snapshots from the "FLARE-VM.testing" VM |
36 |
| - vbox-export-snapshots.py "FLARE-VM.testing" --snapshot "FLARE-VM,.dynamic,Windows 10 VM with FLARE-VM default configuration" --snapshot "FLARE-VM.full,.full.dynamic,Windows 10 VM with FLARE-VM default configuration + 'visualstudio.vm' + 'pdbs.pdbresym.vm'" --snapshot "FLARE-VM.EDU,.EDU,Windows 10 VM with FLARE-VM default configuration + FLARE-EDU materials" |
37 |
| -
|
38 |
| - # Export the "DONE" snapshot from the "REMnux.testing" VM with name "REMnux" |
39 |
| - vbox-export-snapshots.py "REMnux.testing" --exported_vm_name "REMnux" --snapshot "DONE,.dynamic,REMnux (based on Ubuntu) with improved configuration" |
| 33 | + # Export snapshots using the information in the "configs/export_win10_flare-vm.json" config file |
| 34 | + ./vbox-export-snapshots.py configs/export_win10_flare-vm.json |
40 | 35 | """
|
41 | 36 | )
|
42 | 37 |
|
|
48 | 43 | LONG_WAIT = "... (it will take some time, go for an 🍦!)"
|
49 | 44 |
|
50 | 45 |
|
| 46 | +# Format of snapshot information in the configuration file whose path is provided as argument |
| 47 | +snapshotsSchema = { |
| 48 | + "type": "object", |
| 49 | + "properties": { |
| 50 | + "VM_NAME": {"type": "string"}, |
| 51 | + "EXPORTED_VM_NAME": {"type": "string"}, |
| 52 | + "SNAPSHOTS": { |
| 53 | + "type": "array", |
| 54 | + "items": {"type": "array", "items": {"type": "string"}, "minItems": 3, "maxItems": 3}, |
| 55 | + "minItems": 1, |
| 56 | + }, |
| 57 | + "EXPORT_DIR_NAME": {"type": "string"}, |
| 58 | + }, |
| 59 | + "required": ["VM_NAME", "EXPORTED_VM_NAME", "SNAPSHOTS"], |
| 60 | +} |
| 61 | + |
| 62 | + |
51 | 63 | def sha256_file(filename):
|
52 | 64 | with open(filename, "rb") as f:
|
53 | 65 | return hashlib.file_digest(f, "sha256").hexdigest()
|
@@ -116,7 +128,7 @@ def restore_snapshot(vm_uuid, snapshot_name):
|
116 | 128 | print(f'VM {vm_uuid} ✨ restored snapshot "{snapshot_name}"')
|
117 | 129 |
|
118 | 130 |
|
119 |
| -def export_snapshots(vm_name, export_dir_name, exported_vm_name, snapshots): |
| 131 | +def export_snapshots(vm_name, exported_vm_name, snapshots, export_dir_name): |
120 | 132 | date = datetime.today().strftime("%Y%m%d")
|
121 | 133 |
|
122 | 134 | vm_uuid = get_vm_uuid(vm_name)
|
@@ -187,31 +199,36 @@ def main(argv=None):
|
187 | 199 | epilog=EPILOG,
|
188 | 200 | formatter_class=argparse.RawDescriptionHelpFormatter,
|
189 | 201 | )
|
190 |
| - parser.add_argument("vm_name", help="name of the VM to export snapshots from.") |
191 |
| - parser.add_argument("--exported_vm_name", help="name of the exported VMs.", default="FLARE-VM") |
192 | 202 | parser.add_argument(
|
193 |
| - "--export_dir_name", |
194 |
| - help="name of the directory in HOME to export the VMs. The directory is created if it does not exist.", |
195 |
| - default="EXPORTED VMS", |
196 |
| - ) |
197 |
| - parser.add_argument( |
198 |
| - "--snapshot", |
199 |
| - type=lambda s: s.split(","), |
200 |
| - action="append", |
| 203 | + "config_path", |
201 | 204 | help=textwrap.dedent(
|
202 | 205 | """
|
203 |
| - comma-separated list of strings with the information of the snapshot to export: "<SNAPSHOT_NAME>,<EXPORTED_VM_EXTENSION>,<DESCRIPTION>". |
204 |
| - <SNAPSHOT_NAME> is the name of the snapshot to export. |
205 |
| - <EXPORTED_VM_EXTENSION> is appended to the exported VM name: "<EXPORTED_VM_NAME>.<DATE>.<EXPORTED_VM_EXTENSION>". |
206 |
| - <DESCRIPTION> is used as the appliance description. |
207 |
| - Example: "FLARE-VM,.dynamic,Windows 10 VM with FLARE-VM default configuration installed". |
208 |
| - This argument can be provided more than one to export several snapshots, every of them in a different appliance. |
209 |
| - """ |
| 206 | + path of the JSON configuration file. |
| 207 | + "VM_NAME" is the name of the VM to export snapshots from. |
| 208 | + Example: "FLARE-VM.testing". |
| 209 | + "EXPORTED_VM_NAME" is the name of the exported VMs. |
| 210 | + Example: "FLARE-VM". |
| 211 | + "SNAPSHOTS" is a list of lists with information of the snapshots to export: ["SNAPSHOT_NAME", "EXPORTED_VM_EXTENSION", "DESCRIPTION"]. |
| 212 | + Example: ["FLARE-VM", ".dynamic", "Windows 10 VM with FLARE-VM default configuration"]. |
| 213 | + "EXPORT_DIR_NAME" (optional) is the name of the directory in HOME to export the VMs. |
| 214 | + The directory is created if it does not exist. |
| 215 | + Default: "EXPORTED VMS". |
| 216 | + """ |
210 | 217 | ),
|
211 | 218 | )
|
212 | 219 | args = parser.parse_args(args=argv)
|
213 | 220 |
|
214 |
| - export_snapshots(args.vm_name, args.export_dir_name, args.exported_vm_name, args.snapshot) |
| 221 | + try: |
| 222 | + with open(args.config_path) as f: |
| 223 | + config = json.load(f) |
| 224 | + |
| 225 | + jsonschema.validate(instance=config, schema=snapshotsSchema) |
| 226 | + except Exception as e: |
| 227 | + print(f'Invalid "{args.config_path}": {e}') |
| 228 | + exit() |
| 229 | + |
| 230 | + export_dir_name = config.get("EXPORT_DIR_NAME", "EXPORTED VMS") |
| 231 | + export_snapshots(config["VM_NAME"], config["EXPORTED_VM_NAME"], config["SNAPSHOTS"], export_dir_name) |
215 | 232 |
|
216 | 233 |
|
217 | 234 | if __name__ == "__main__":
|
|
0 commit comments