forked from SecGen/SecGen
-
Notifications
You must be signed in to change notification settings - Fork 318
/
secgen.rb
750 lines (681 loc) · 26.7 KB
/
secgen.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
require 'getoptlong'
require 'fileutils'
require 'nori'
require 'open3'
require 'nokogiri/class_resolver'
require 'nokogiri'
require_relative 'lib/helpers/constants.rb'
require_relative 'lib/helpers/print.rb'
require_relative 'lib/helpers/gem_exec.rb'
require_relative 'lib/helpers/ovirt.rb'
require_relative 'lib/helpers/proxmox.rb'
require_relative 'lib/readers/system_reader.rb'
require_relative 'lib/readers/module_reader.rb'
require_relative 'lib/output/project_files_creator.rb'
# Displays secgen usage data
def usage
Print.std "Usage:
#{$0} [--options] <command>
OPTIONS:
--scenario [xml file], -s [xml file]: Set the scenario to use
(defaults to #{SCENARIO_XML})
--project [output dir], -p [output dir]: Directory for the generated project
(output will default to #{default_project_dir})
--shutdown: Shutdown VMs after provisioning (vagrant halt)
--network-ranges: Override network ranges within the scenario, use a comma-separated list
--forensic-image-type [image type]: Forensic image format of generated image (raw, ewf)
--read-options [conf path]: Reads options stored in file as arguments (see example.conf)
--memory-per-vm: Allocate generated VMs memory in MB (e.g. --memory-per-vm 1024)
--total-memory: Allocate total VM memory for the scenario, split evenly across all VMs.
--cpu-cores: Number of virtual CPUs for generated VMs
--help, -h: Shows this usage information
--system, -y [system_name]: Only build this system_name from the scenario
--snapshot: Creates a snapshot of VMs once built
--no-tests: Prevent post-provisioning tests from running.
--no-destroy-on-failure: Don't delete VMs that fail to build (except when retrying).
--retries [number]: Retry building vms that fail to build this many attempts.
--no-parallel: Build one VM at a time.
VIRTUALBOX OPTIONS:
--gui-output, -g: Show the running VM (not headless)
--nopae: Disable PAE support
--hwvirtex: Enable HW virtex support
--vtxvpid: Enable VTX support
--max-cpu-usage [1-100]: Controls how much cpu time a virtual CPU can use
(e.g. 50 implies a single virtual CPU can use up to 50% of a single host CPU)
OVIRT OPTIONS:
--ovirtuser [ovirt_username]
--ovirtpass [ovirt_password]
--ovirt-url [ovirt_api_url]
--ovirtauthz [ovirt authz]
--ovirt-cluster [ovirt_cluster]
--ovirt-network [ovirt_network_name]
--ovirt-affinity-group [ovirt_affinity_group_name]
ESXI OPTIONS:
--esxiuser [esxi_username]
--esxipass [esxi_password]
--esxi-hostname [esxi_api_url]
(ESXi hostname/IP)
--esxi-datastore [esxi_datastore]
--esxi-disktype [esxi_disktype]: 'thin', 'thick', or 'eagerzeroedthick'
(If unspecified, it will be set to 'thin')
--esxi-network [esxi_network_name]
(If it's not specified, the default is to use the first found)
--esxi-guest-nictype [esxi_nictype]: 'e1000', 'e1000e', 'vmxnet', 'vmxnet2', 'vmxnet3', 'Vlance', or 'Flexible'
(RISKY - Can cause VM to not respond)
--esxi-no-hostname
(Setting the hostname on some boxes can cause 'vagrant up' to fail if the network configuration wasn't previously cleaned up.)
PROXMOX OPTIONS:
--proxmoxuser [username]
--proxmoxpass [password]
--proxmox-url [api_url]
--proxmox-node [node]
--proxmox-network [proxmox network name]
--proxmox-vlan [vlan number]
COMMANDS:
run, r: Builds project and then builds the VMs
build-project, p: Builds project (vagrant and puppet config), but does not build VMs
build-vms, v: Builds VMs from a previously generated project
(use in combination with --project [dir])
ovirt-post-build: only performs the ovirt actions that normally follow a successful vm build
(snapshots and networking)
proxmox-post-build: only performs the proxmox actions that normally follow a successful vm build
(snapshots and networking)
create-forensic-image: Builds forensic images from a previously generated project
(can be used in combination with --project [dir])
list-scenarios: Lists all scenarios that can be used with the --scenario option
list-projects: Lists all projects that can be used with the --project option
delete-all-projects: Deletes all current projects in the projects directory
"
exit
end
# Builds the vagrant configuration file based on a scenario file
# @return build_number [Integer] Current project's build number
def build_config(scenario, out_dir, options)
Print.info 'Reading configuration file for virtual machines you want to create...'
# read the scenario file describing the systems, which contain vulnerabilities, services, etc
# this returns an array/hashes structure
systems = SystemReader.read_scenario(scenario, options)
Print.std "#{systems.size} system(s) specified"
all_available_modules = ModuleReader.get_all_available_modules
Print.info 'Resolving systems: randomising scenario...'
# update systems with module selections
systems.map! {|system|
system.module_selections = system.resolve_module_selection(all_available_modules, options)
system
}
Print.info "Creating project: #{out_dir}..."
# creates Vagrantfile and other outputs and starts the vagrant installation
creator = ProjectFilesCreator.new(systems, out_dir, scenario, options)
creator.write_files
Print.info 'Project files created.'
end
# Builds the vm via the vagrant file in the project dir
# @param project_dir
def build_vms(scenario, project_dir, options)
unless project_dir.include? ROOT_DIR
Print.info 'Relative path to project detected'
project_dir = "#{ROOT_DIR}/#{project_dir}"
Print.info "Using #{project_dir}"
end
project_dir + '/scenario.xml'
Print.info "Building project: #{project_dir}"
system = ''
command = 'up'
if options.has_key? :system
system = options[:system]
end
if options.has_key? :reload
command = '--provision reload'
end
if options.has_key? :noparallel
command = "#{command} --no-parallel"
end
# retry count, for when things fail to build
retry_count = options.has_key?(:retries) ? options[:retries].to_i : 0
successful_creation = false
while retry_count >= 0 and !successful_creation
vagrant_output = GemExec.exe('vagrant', project_dir, "#{command} #{system}")
if vagrant_output[:status] == 0 and post_provision_tests(project_dir, options)
Print.info 'VMs created.'
successful_creation = true
if options[:shutdown] or OVirtFunctions::provider_ovirt?(options) or ProxmoxFunctions::provider_proxmox?(options)
Print.info 'Shutting down VMs.'
sleep(30)
GemExec.exe('vagrant', project_dir, 'halt')
end
else
if retry_count > 0
# Identify which VMs failed
if vagrant_output[:exception].class == ProcessHelper::UnexpectedExitStatusError
split = vagrant_output[:output].split('==> ')
failures_to_destroy = []
split.each do |line|
if match = line.match(/^([-a-zA-Z_0-9]+):[^:]+An error occured/i)
vm_to_destroy = match.captures[0]
failures_to_destroy << vm_to_destroy
elsif match = line.match(/^([-a-zA-Z_0-9]+):[^:]+Error:/i)
vm_to_destroy = match.captures[0]
failures_to_destroy << vm_to_destroy
elsif match = line.match(/^([-a-zA-Z_0-9]+):[^:]+VM is not created/i)
vm_not_to_destroy = match.captures[0]
Print.err "Not going to destroy #{vm_not_to_destroy}, since it does not exist"
failures_to_destroy.delete_if {|x| x == vm_not_to_destroy}
# TODO: not sure if there is a need to remove_uncreated_vms() here too? (I don't think so?)
end # TODO: Add another elsif here to check if any tests have failed, edit the output of the tests so that it has a unique string that captures the vm name
end
failures_to_destroy = failures_to_destroy.uniq
if failures_to_destroy.size == 0
Print.err 'Failed. Not retrying. Please refer to the error above.'
exit 1
end
Print.err 'Error creating VMs [' + failures_to_destroy.join(',') + '] destroying VMs and retrying...'
failures_to_destroy.each do |failed_vm|
destroy = 'destroy ' + failed_vm + ' -f'
destroy_output = GemExec.exe('vagrant', project_dir, destroy)
if destroy_output[:status] == 0
if !destroy_output[:output].include? 'VM is not created. Please run `vagrant up` first.'
Print.info "vagrant #{destroy} completed successfully."
else
OVirtFunctions::remove_uncreated_vms(destroy_output[:output], options, scenario)
# Add ESXI destroy uncreated VMs
end
else
Print.err "Failed to destroy #{failed_vm}. Exiting."
exit 1
end
sleep(10)
end
else # TODO: elsif vagrant_output[:exception].type == ProcessHelper::TimeoutError >destroy individually broken vms as above?
Print.err 'Vagrant up timeout, destroying VMs and retrying...'
GemExec.exe('vagrant', project_dir, 'destroy -f')
end
else
if options[:nodestroy]
Print.err "Not destroying failed VM."
else
Print.err 'Error provisioning VMs, destroying VMs and exiting SecGen.'
GemExec.exe('vagrant', project_dir, 'destroy -f')
end
exit 1
end
end
retry_count -= 1
end
if successful_creation
if OVirtFunctions.provider_ovirt?(options)
ovirt_post_build(options, scenario, project_dir)
elsif ProxmoxFunctions.provider_proxmox?(options)
proxmox_post_build(options, scenario, project_dir)
elsif options[:snapshot]
# snapshots happen in the above post_build functions
# VirtualBox snapshots
Print.info 'Creating a snapshot of VM(s)'
sleep(10) # give oVirt/Virtualbox a chance to save any VM config changes before creating the snapshot
GemExec.exe('vagrant', project_dir, 'snapshot push')
end
else
Print.err "Failed to build VMs"
show_running_time(beginning_time)
exit 1
end
end
# actions on the VMs after vagrant has built them
# this includes networking and snapshots
def ovirt_post_build(options, scenario, project_dir)
Print.std 'Taking oVirt post-build actions...'
if options[:ovirtnetwork]
Print.info 'Assigning network(s) of VM(s)'
OVirtFunctions::assign_networks(options, scenario, get_vm_names(scenario))
end
if options[:ovirtaffinitygroup]
Print.info 'Assigning affinity group of VM(s)'
OVirtFunctions::assign_affinity_group(options, scenario, get_vm_names(scenario))
end
if options[:snapshot]
Print.info 'Creating a snapshot of VM(s)'
sleep(10) # give oVirt/Virtualbox a chance to save any VM config changes before creating the snapshot
OVirtFunctions::create_snapshot(options, scenario, get_vm_names(scenario))
end
end
# actions on the VMs after vagrant has built them
# this includes networking and snapshots
def proxmox_post_build(options, scenario, project_dir)
Print.std 'Taking Proxmox post-build actions...'
if options[:proxmoxnetwork]
Print.info 'Assigning network(s) of VM(s)'
ProxmoxFunctions::assign_networks(project_dir, get_vm_names(scenario), options)
end
if options[:snapshot]
Print.info 'Creating a snapshot of VM(s)'
sleep(1) # give oVirt/Virtualbox a chance to save any VM config changes before creating the snapshot
ProxmoxFunctions::create_snapshot(project_dir, get_vm_names(scenario), options)
end
end
# Make forensic image helper methods
#################################################
# Create an EWF forensic image
#
# @author Jason Keighley
# @return [Void]
def create_ewf_image(drive_path, image_output_location)
## Make E01 image
Print.info "Creating E01 image with path #{image_output_location}.E01"
Print.info 'This may take a while:'
Print.info "E01 image #{image_output_location}.E01 created" if system "ftkimager '#{drive_path}' '#{image_output_location}' --e01"
end
# Create an DD forensic image
#
# @author Jason Keighley
# @return [Void]
def create_dd_image(drive_path, image_output_location)
## Make DD image
Print.info "Creating dd image with path #{image_output_location}.raw"
Print.info 'This may take a while:'
Print.info "Raw image #{image_output_location}.raw created" if system "VBoxManage clonemedium disk '#{drive_path}' '#{image_output_location}.raw' --format RAW"
end
# Delete virtualbox virtual machine
#
# @author Jason Keighley
# @param [String] vm_name Virtual machine name in VirtualBox
# @return [Void]
def delete_virtualbox_vm(vm_name)
Print.info "Deleting VirtualBox VM #{vm_name}"
Print.info "VirtualBox VM #{vm_name} deleted" if system "VBoxManage unregistervm #{vm_name} --delete"
end
# Make forensic image helper methods \end
#################################################
def make_forensic_image(project_dir, image_output_location, image_type)
drive_path = %x(VBoxManage list hdds | grep '#{project_dir.split('/').last}').sub(/\ALocation:\s*/, '').sub(/\n/, '')
drive_name = drive_path.split('/').last
image_output_location = "#{project_dir}/#{drive_name}".sub(/.vmdk|.vdi/, '') unless image_output_location
## Ensure all vms are shutdown
system "cd '#{project_dir}' && vagrant halt"
case image_type.downcase
when 'raw', 'dd'
create_dd_image(drive_path, image_output_location)
when 'ewf', 'e01'
create_ewf_image(drive_path, image_output_location)
else
Print.info "The image type [#{image_type}] is not recognised."
end
end
# Runs methods to run and configure a new vm from the configuration file
def run(scenario, project_dir, options)
build_config(scenario, project_dir, options)
build_vms(scenario, project_dir, options)
end
def default_project_dir
"#{PROJECTS_DIR}/SecGen#{Time.new.strftime("%Y%m%d_%H%M%S")}"
end
def project_dir(prefix)
"#{PROJECTS_DIR}/#{prefix}_SecGen#{Time.new.strftime("%Y%m%d_%H%M%S")}"
end
def list_scenarios
Print.std "Full paths to scenario files are displayed below"
Dir["#{ROOT_DIR}/scenarios/**/*"].select {|file| !File.directory? file}.each_with_index do |scenario_name, scenario_number|
Print.std "#{scenario_number}) #{scenario_name}"
end
end
def list_projects
Print.std "Full paths to project directories are displayed below"
Dir["#{PROJECTS_DIR}/*"].select {|file| !File.file? file}.each_with_index do |scenario_name, scenario_number|
Print.std "#{scenario_number}) #{scenario_name}"
end
end
# Delete all current project directories
#
# @author Jason Keighley
# @return [Void]
def delete_all_projects
FileUtils.rm_r(Dir.glob("#{PROJECTS_DIR}/*"))
end
# returns an array containing the system names from the scenario
def get_vm_names(scenario)
vm_names = []
parser = Nori.new
scenario_hash = parser.parse(File.read(scenario))
# Print.debug "scenario_hash: #{scenario_hash}"
if scenario_hash.key?('scenario') # work around for a parsing quirk
scenario_hash = scenario_hash['scenario']
end
if scenario_hash['system'].is_a? Array
scenario_hash['system'].each do |system|
vm_names << system['system_name']
end
elsif scenario_hash['system'].is_a? Hash
vm_names << scenario_hash['system']['system_name']
else
Print.debug "Not an array or hash?: #{scenario_hash['system']}"
end
vm_names
end
def reboot_cycle(project_dir)
Print.info 'Shutting down VMs.'
sleep(30)
GemExec.exe('vagrant', project_dir, 'halt')
sleep 5
GemExec.exe('vagrant', project_dir, 'up --no-provision')
sleep 45
end
def post_provision_tests(project_dir, options)
tests_passed = true
unless options[:notests]
Print.info 'Restarting for post-provision tests...'
reboot_cycle(project_dir)
Print.info 'Running post-provision tests...'
test_module_outputs = []
test_script_paths = Dir.glob("#{project_dir}/puppet/*/modules/*/secgen_test/*.rb")
test_script_paths.each do |test_file_path|
test_stdout, test_stderr, test_status = Open3.capture3("bundle exec ruby #{test_file_path}")
test_module_outputs << {:stdout => test_stdout.split("\n"), :stderr => test_stderr, :exit_status => test_status}
end
test_module_outputs.each do |test_output|
if test_output[:exit_status].exitstatus != 0
tests_passed = false
Print.err test_output[:stdout].join("\n")
Print.err "Post provision tests contained failures!"
Print.err test_output[:stderr]
else
Print.info test_output[:stdout].join("\n")
end
end
end
tests_passed
end
def show_running_time(beginning_time)
finish_time = Time.now
lapsed_time = finish_time - beginning_time
remainder, secs = lapsed_time.divmod(60)
remainder, mins = remainder.divmod(60)
days, hours = remainder.divmod(24)
printable = ""
printable << "#{days}d " if days > 0
printable << "#{hours}h " if hours > 0
printable << "#{mins}m " if mins > 0
printable << "#{secs.round}s"
Print.info "Completed in #{printable}"
end
# end of method declarations
# start of program execution
Print.std '~' * 47
Print.std 'SecGen - Creates virtualised security scenarios'
Print.std ' Licensed GPLv3 2014-24'
Print.std '~'*47
Print.debug "\nPlease take a minute to tell us how you are using SecGen:"
Print.debug "https://tinyurl.com/SecGenFeedback\n"
beginning_time = Time.now
# Add read-options from config file (needs handling before options parsed by GetoptLong)
if ARGV.include? '--read-options'
index = ARGV.find_index('--read-options')
conf_path = ARGV[index + 1]
# remove --read-options and conf_path
ARGV.delete_at(index)
ARGV.delete_at(index)
conf_data = File.read(conf_path).split(' ')
ARGV.unshift(*conf_data)
end
# Get command line arguments
opts = GetoptLong.new(
['--help', '-h', GetoptLong::NO_ARGUMENT],
['--project', '-p', GetoptLong::REQUIRED_ARGUMENT],
['--scenario', '-s', GetoptLong::REQUIRED_ARGUMENT],
['--prefix', GetoptLong::REQUIRED_ARGUMENT],
['--system', '-y', GetoptLong::REQUIRED_ARGUMENT],
['--reload', '-r', GetoptLong::NO_ARGUMENT],
['--gui-output', '-g', GetoptLong::NO_ARGUMENT],
['--nopae', GetoptLong::NO_ARGUMENT],
['--hwvirtex', GetoptLong::NO_ARGUMENT],
['--vtxvpid', GetoptLong::NO_ARGUMENT],
['--memory-per-vm', GetoptLong::REQUIRED_ARGUMENT],
['--total-memory', GetoptLong::REQUIRED_ARGUMENT],
['--cpu-cores', GetoptLong::REQUIRED_ARGUMENT],
['--max-cpu-usage', GetoptLong::REQUIRED_ARGUMENT],
['--shutdown', GetoptLong::NO_ARGUMENT],
['--network-ranges', GetoptLong::REQUIRED_ARGUMENT],
['--forensic-image-type', GetoptLong::REQUIRED_ARGUMENT],
['--snapshot', GetoptLong::NO_ARGUMENT],
['--no-tests', GetoptLong::NO_ARGUMENT],
['--no-destroy-on-failure', GetoptLong::NO_ARGUMENT],
['--no-parallel', GetoptLong::NO_ARGUMENT],
['--retries', GetoptLong::REQUIRED_ARGUMENT],
['--ovirtuser', GetoptLong::REQUIRED_ARGUMENT],
['--ovirtpass', GetoptLong::REQUIRED_ARGUMENT],
['--ovirt-url', GetoptLong::REQUIRED_ARGUMENT],
['--ovirtauthz', GetoptLong::REQUIRED_ARGUMENT],
['--ovirt-cluster', GetoptLong::REQUIRED_ARGUMENT],
['--ovirt-network', GetoptLong::REQUIRED_ARGUMENT],
['--ovirt-affinity-group', GetoptLong::REQUIRED_ARGUMENT],
['--proxmoxuser', GetoptLong::REQUIRED_ARGUMENT],
['--proxmoxpass', GetoptLong::REQUIRED_ARGUMENT],
['--proxmox-url', GetoptLong::REQUIRED_ARGUMENT],
['--proxmox-node', GetoptLong::REQUIRED_ARGUMENT],
['--proxmox-network', GetoptLong::REQUIRED_ARGUMENT],
['--proxmox-vlan', GetoptLong::REQUIRED_ARGUMENT],
['--esxiuser', GetoptLong::REQUIRED_ARGUMENT],
['--esxipass', GetoptLong::REQUIRED_ARGUMENT],
['--esxi-hostname', GetoptLong::REQUIRED_ARGUMENT],
['--esxi-datastore', GetoptLong::REQUIRED_ARGUMENT],
['--esxi-network', GetoptLong::REQUIRED_ARGUMENT],
['--esxi-disktype', GetoptLong::REQUIRED_ARGUMENT],
['--esxi-guest-nictype', GetoptLong::REQUIRED_ARGUMENT],
['--esxi-no-hostname', GetoptLong::NO_ARGUMENT],
)
scenario = SCENARIO_XML
project_dir = nil
options = {}
# process option arguments
opts.each do |opt, arg|
case opt
# Main options
when '--help'
usage
when '--scenario'
scenario = arg;
when '--project'
project_dir = arg;
when '--prefix'
options[:prefix] = arg
project_dir = project_dir(arg)
# Additional options
when '--system'
Print.info "VM control (Vagrant) commands will only apply to system #{arg} (must match a system defined in the scenario)"
options[:system] = arg
when '--reload'
Print.info "Will reload and re-provision the VMs"
options[:reload] = true
when '--gui-output'
Print.info "Gui output set (virtual machines will be spawned)"
options[:gui_output] = true
when '--nopae'
Print.info "no pae"
options[:nopae] = true
when '--hwvirtex'
Print.info "with HW virtualisation"
options[:hwvirtex] = true
when '--vtxvpid'
Print.info "with VT support"
options[:vtxvpid] = true
when '--memory-per-vm'
if options.has_key? :total_memory
Print.info 'Total memory option specified before memory per vm option, defaulting to total memory value'
else
Print.info "Memory per vm set to #{arg}"
options[:memory_per_vm] = arg
end
when '--total-memory'
if options.has_key? :memory_per_vm
Print.info 'Memory per vm option specified before total memory option, defaulting to memory per vm value'
else
Print.info "Total memory to be used set to #{arg}"
options[:total_memory] = arg
end
when '--cpu-cores'
Print.info "Number of cpus to be used set to #{arg}"
options[:cpu_cores] = arg
when '--max-cpu-usage'
Print.info "Max CPU usage set to #{arg}"
options[:max_cpu_usage] = arg
when '--shutdown'
Print.info 'Shutdown VMs after provisioning'
options[:shutdown] = true
when '--network-ranges'
Print.info 'Overriding Network Ranges'
options[:ip_ranges] = arg.split(',')
when '--forensic-image-type'
Print.info "Image output type set to #{arg}"
options[:forensic_image_type] = arg
when '--snapshot'
Print.info "Taking snapshots when VMs are created"
options[:snapshot] = true
# oVirt options
when '--ovirtuser'
Print.info "Ovirt Username : #{arg}"
options[:ovirtuser] = arg
when '--ovirtpass'
Print.info "Ovirt Password : ********"
options[:ovirtpass] = arg
when '--ovirt-url'
Print.info "Ovirt API url : #{arg}"
options[:ovirturl] = arg
when '--ovirtauthz'
Print.info "Ovirt Authz: #{arg}"
options[:ovirtauthz] = arg
when '--ovirt-cluster'
Print.info "Ovirt Cluster : #{arg}"
options[:ovirtcluster] = arg
when '--ovirt-network'
Print.info "Ovirt Network Name : #{arg}"
options[:ovirtnetwork] = arg
when '--ovirt-affinity-group'
Print.info "Ovirt Affinity Group : #{arg}"
options[:ovirtaffinitygroup] = arg
# Proxmox options
when '--proxmoxuser'
Print.info "Proxmox Username : #{arg}"
options[:proxmoxuser] = arg
when '--proxmoxpass'
Print.info "Proxmox Password : ********"
options[:proxmoxpass] = arg
when '--proxmox-url'
Print.info "Proxmox API url : #{arg}"
options[:proxmoxurl] = arg
when '--proxmox-node'
Print.info "Proxmox node : #{arg}"
options[:proxmoxnode] = arg
when '--proxmox-network'
Print.info "Proxmox Network Name : #{arg}"
options[:proxmoxnetwork] = arg
when '--proxmox-vlan'
Print.info "Proxmox Network VLAN : #{arg}"
options[:proxmoxvlan] = arg
# ESXi options
when '--esxiuser'
Print.info "ESXi Username : #{arg}"
options[:esxiuser] = arg
when '--esxipass'
Print.info "ESXi Password : ********"
options[:esxipass] = arg
when '--esxi-hostname'
Print.info "ESXi host : #{arg}"
options[:esxi_host] = arg
when '--esxi-datastore'
Print.info "ESXi datastore: #{arg}"
options[:esxidatastore] = arg
when '--esxi-network'
Print.info "ESXi Network Name : #{arg}"
options[:esxinetwork] = arg
when '--esxi-disktype'
if ['thin', 'thick', 'eagerzeroedthick'].include? arg
Print.info "ESXi disk type : #{arg}"
options[:esxidisktype] = arg
else
Print.warn "ESXi disk type : #{arg} not supported"
Print.warn "ESXi disk type only supports: 'thin', 'thick', or 'eagerzeroedthick'"
Print.info "Defaulting to ESXi disk type: 'thin'"
options[:esxidisktype] = 'thin'
end
when '--esxi-guest-nictype'
if ['e1000', 'e1000e', 'vmxnet','vmxnet2', 'vmxnet3', 'Vlance','Flexible'].include? arg
Print.info "ESXi Guest Nic type : #{arg}"
options[:esxiguestnictype] = arg
else
Print.warn "ESXi guest nic type : #{arg} not supported"
Print.warn "ESXi guest nic type only supports: 'e1000', 'e1000e', 'vmxnet','vmxnet2', 'vmxnet3', 'Vlance','Flexible'"
Print.info "Defaulting to ESXi disk type: 'thin'"
end
when '--esxi-no-hostname'
Print.info "Not setting hostnames when VMs are created"
options[:esxinohostname] = true
when '--no-tests'
Print.info "Not running post-provision tests"
options[:notests] = true
when '--no-destroy-on-failure'
Print.info "Will not destroy VMs when they fail to build"
options[:nodestroy] = true
when '--no-parallel'
Print.info "Will not build VMs in parallel"
options[:noparallel] = true
when '--retries'
Print.info "Number of retries to build vms : #{arg}"
options[:retries] = arg
else
Print.err "Argument not valid: #{arg}"
usage
exit 1
end
end
# at least one command
if ARGV.length < 1
Print.err 'Missing command'
usage
exit 1
end
# process command
case ARGV[0]
when 'run', 'r'
project_dir = default_project_dir unless project_dir
run(scenario, project_dir, options)
when 'build-project', 'p'
project_dir = default_project_dir unless project_dir
build_config(scenario, project_dir, options)
when 'build-vms', 'v'
if project_dir
build_vms(scenario, project_dir, options)
else
Print.err 'Please specify project directory to read'
usage
exit 1
end
when 'create-forensic-image'
image_type = options.has_key?(:forensic_image_type) ? options[:forensic_image_type] : 'raw';
if project_dir
build_vms(scenario, project_dir, options)
make_forensic_image(project_dir, nil, image_type)
else
project_dir = default_project_dir unless project_dir
build_config(scenario, project_dir, options)
build_vms(scenario, project_dir, options)
make_forensic_image(project_dir, nil, image_type)
end
when 'esxi-post-build'
esxi_post_build(options, scenario, project_dir)
when 'ovirt-post-build'
ovirt_post_build(options, scenario, project_dir)
when 'proxmox-post-build'
proxmox_post_build(options, scenario, project_dir)
when 'list-scenarios'
list_scenarios
exit 0
when 'list-projects'
list_projects
exit 0
when 'delete-all-projects'
delete_all_projects
Print.std 'All projects deleted'
exit 0
else
Print.err "Command not valid: #{ARGV[0]}"
usage
exit 1
end
show_running_time(beginning_time)