This commit adds a fog-based driver (so cloud services can easily be used as VMs), an example backtrack5 modifier, a meterpreter modifier (so the framework / meterpreter can be used as a command and control driver) and various cleanups to all drivers.

git-svn-id: file:///home/svn/framework3/trunk@13658 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
Jonathan Cran 2011-08-29 16:03:28 +00:00
parent 5fa7ddf5f4
commit 1b4dc17e7f
16 changed files with 526 additions and 232 deletions

View File

@ -0,0 +1,6 @@
module Lab
module Controllers
module FogController
end
end
end

View File

@ -1,10 +1,10 @@
require 'controller/workstation_controller'
require 'controller/workstation_vixr_controller'
require 'controller/remote_workstation_controller'
require 'controller/virtualbox_controller'
require 'controller/fog_controller'
require 'controller/dynagen_controller'
require 'controller/remote_workstation_controller'
require 'controller/remote_esx_controller'
#require 'controller/qemu_controller'
#require 'controller/qemudo_controller'
#require 'controller/amazon_controller'
#require 'controller/fog_controller'

View File

@ -12,23 +12,10 @@ require 'vm_driver'
module Lab
module Drivers
class DynagenDriver < VmDriver
attr_accessor :type
attr_accessor :location
def initialize(vmid,location,platform)
@vmid = filter_command(vmid)
@location = filter_command(location)
if !File.exist?(location)
raise ArgumentError,"Couldn't find: " + location
end
@type = "dynagen"
def initialize(config,dynagen_config)
super(config)
@running = false
@platform = filter_command(platform)
@credentials = []
@dynagen_platform = filter_command(dynagen_config['dynagen_platform'])
end
def start
@ -36,7 +23,7 @@ module Drivers
# and set the autostart property
## start background dynamips process
system_command("dynamips -H #{@platform} &")
system_command("dynamips -H #{@dynagen_platform} &")
system_command("dynagen #{@location}")
@running = true
end

View File

@ -0,0 +1,166 @@
require 'vm_driver'
##
## $Id$
##
module Lab
module Drivers
class FogDriver < VmDriver
def initialize(config,fog_config)
super(config)
@fog_config = fog_config
puts "Fog Config: #{fog_config}"
# Soft dependency
begin
require 'fog'
rescue LoadError
raise "WARNING: Library fog not found. Could Not Create Driver"
end
if @fog_config['fog_type'] == "ec2"
# AWS / EC2 Base Credential Configuration
@aws_cert_file = IO.read(fog_config['fog_aws_cert_file']).chomp if fog_config['fog_aws_cert_file']
@aws_private_key_file = IO.read(fog_config['fog_aws_private_key_file']).chomp if fog_config['fog_aws_private_key_file']
@ec2_access_key_file = IO.read(fog_config['fog_ec2_access_key_file']).chomp if fog_config['fog_ec2_access_key_file']
@ec2_secret_access_key_file = IO.read(fog_config['fog_ec2_secret_access_key_file']).chomp if fog_config['fog_ec2_secret_access_key_file']
# Instance Keys
@ec2_instance_public_key_file = IO.read(fog_config['fog_ec2_instance_public_key_file']).chomp if fog_config['fog_ec2_instance_public_key_file']
@ec2_instance_private_key_file = IO.read(fog_config['fog_ec2_instance_private_key_file']).chomp if fog_config['fog_ec2_instance_private_key_file']
# Instance Details
@ec2_base_ami = fog_config['fog_ec2_base_ami']
@ec2_flavor = fog_config['fog_ec2_flavor']
@ec2_user = fog_config['fog_ec2_user']
@ec2_region = fog_config['fog_ec2_region']
# Set up a connection
@compute = Fog::Compute.new(
:provider => "Aws",
:aws_access_key_id => @aws_access_key_file,
:aws_secret_access_key => @aws_secret_access_key_file )
else
raise "Unsupported Fog Type"
end
end
def start
ec2_settings = {
:image_id => @ec2_base_ami,
:flavor_id => @ec2_flavor,
:public_key_path => @ec2_instance_public_key_file,
:private_key_path => @ec2_instance_private_key_file,
:username => @ec2_user}
begin
@fog_server = @compute.servers.bootstrap(ec2_settings)
rescue Fog::Compute::AWS::Error => e
raise "Couldn't authenticate to AWS - did you place keys in the creds/ directory?"
exit
end
end
def stop
@fog_server.destroy
end
def suspend
raise "unimplemented"
end
def pause
raise "unimplemented"
end
def reset
raise "unimplemented"
end
def create_snapshot(snapshot)
raise "unimplemented"
end
def revert_snapshot(snapshot)
raise "unimplemented"
end
def delete_snapshot(snapshot)
raise "unimplemented"
end
=begin
def run_command(command)
## vm_driver will need a little patching for this to work, as
## amis use keys for auth. i think it's just a matter of not passing the
## password to ssh_exec. So maybe the thing to do is have a ssh_key_exec
## function in vm_driver.rb that does the right thing.
script_rand_name = rand(10000)
if @os == "windows"
local_tempfile_path = "/tmp/lab_script_#{script_rand_name}.bat"
remote_tempfile_path = "C:\\\\lab_script_#{script_rand_name}.bat"
remote_run_command = remote_tempfile_path
else
local_tempfile_path = "/tmp/lab_script_#{script_rand_name}.sh"
remote_tempfile_path = "/tmp/lab_script_#{script_rand_name}.sh"
remote_run_command = "/bin/sh #{remote_tempfile_path}"
end
# write out our script locally
File.open(local_tempfile_path, 'w') {|f| f.write(command) }
# since we can't copy easily w/o tools, let's just run it directly :/
if @os == "linux"
output_file = "/tmp/lab_command_output_#{rand(1000000)}"
scp_to(local_tempfile_path, remote_tempfile_path)
ssh_exec(remote_run_command + "> #{output_file}")
scp_from(output_file, output_file)
ssh_exec("rm #{output_file}")
ssh_exec("rm #{remote_tempfile_path}")
# Ghettohack!
string = File.open(output_file,"r").read
`rm #{output_file}`
else
raise "zomgwtfbbqnotools"
end
end
def copy_from(from, to)
raise "unimplemented"
end
def copy_to(from, to)
raise "unimplemented"
end
def check_file_exists(file)
raise "unimplemented"
end
def create_directory(directory)
raise "unimplemented"
end
=end
def cleanup
@fog_server.destroy
end
def running?
return true #TODO
end
end
end
end

View File

@ -12,24 +12,15 @@ module Drivers
class RemoteEsxDriver < VmDriver
def initialize(vmid,os=nil, tools=false, user=nil, host=nil, credentials=nil)
def initialize(config)
unless user then raise ArgumentError, "Must provide a username" end
unless host then raise ArgumentError, "Must provide a hostname" end
unless config['user'] then raise ArgumentError, "Must provide a username" end
unless config['host'] then raise ArgumentError, "Must provide a hostname" end
@vmid = filter_command(vmid)
@user = filter_command(user)
@host = filter_command(host)
@credentials = credentials # individually filtered
@tools = tools # not used in command lines, no filter
@os = os # not used in command lines, no filter
super(config)
# TODO - Currently only implemented for the first set
if @credentials.count > 0
@vm_user = filter_input(@credentials[0]['user'])
@vm_pass = filter_input(@credentials[0]['pass'])
@vm_keyfile = filter_input(@credentials[0]['keyfile'])
end
@user = filter_command(config['user'])
@host = filter_command(config['host'])
end
def start
@ -62,12 +53,14 @@ class RemoteEsxDriver < VmDriver
#vmware-vim-cmd vmsvc/snapshot.create [vmid: int] [snapshotName: string]
# [snapshotDescription: string] [includeMemory:bool]
system_command("ssh #{@user}@#{@host} \"vim-cmd vmsvc/snapshot.create #{@vmid} #{snapshot} \'lab created snapshot\' 1 true\"")
`ssh #{@user}@#{@host} \"vim-cmd vmsvc/snapshot.create #{@vmid} #{snapshot} \'lab created snapshot\' 1 true\""`
end
def revert_snapshot(snapshot)
raise "Not Implemented"
#vmware-vim-cmd vmsvc/snapshot.revert [vmid: int] [snapshotlevel: int] [snapshotindex: int]
# not sure how we can do this, would have to list snapshots and map name to level & index
@ -87,11 +80,19 @@ class RemoteEsxDriver < VmDriver
end
def copy_from(from, to)
raise "Not Implemented"
if @os == "linux"
scp_from(from, to)
else
raise "Unimplemented"
end
end
def copy_to(from, to)
raise "Not Implemented"
if @os == "linux"
scp_to(from, to)
else
raise "Unimplemented"
end
end
def check_file_exists(file)

View File

@ -11,26 +11,15 @@ class RemoteWorkstationDriver < VmDriver
attr_accessor :location # among other things
def initialize(vmid, location, os=nil, tools=false, user=nil, host=nil, credentials=nil)
def initialize(config)
## TODO - Should proabably check file existence?
unless user then raise ArgumentError, "Must provide a username" end
unless host then raise ArgumentError, "Must provide a hostname" end
unless config['user'] then raise ArgumentError, "Must provide a username" end
unless config['host'] then raise ArgumentError, "Must provide a hostname" end
@vmid = filter_command(vmid)
@location = filter_command(location)
@user = filter_command(user)
@host = filter_command(host)
@credentials = credentials # individually filtered
@tools = tools # not used in command lines, no filter
@os = os # not used in command lines, no filter
super(config)
# TODO - Currently only implemented for the first set
if @credentials.count > 0
@vm_user = filter_input(@credentials[0]['user']) || "\'\'"
@vm_pass = filter_input(@credentials[0]['pass']) || "\'\'"
@vm_keyfile = filter_input(@credentials[0]['keyfile'])
end
@user = filter_command(config['user'])
@host = filter_command(config['host'])
end
def start
@ -183,7 +172,7 @@ class RemoteWorkstationDriver < VmDriver
"createDirectoryInGuest \'#{@location}\' \'#{directory}\' nogui\""
system_command(vmrunstr)
else
ssh_exec(command)
raise "not implemented"
end
end

View File

@ -10,12 +10,10 @@ module Drivers
attr_accessor :location
def initialize(vmid, location=nil, credentials=nil)
def initialize(config)
@vmid = filter_command(vmid)
@location = filter_command(location)
super(config)
## Check to see if we already know this vm, if not, go on location
vmid_list = ::Lab::Controllers::VirtualBoxController::config_list
unless vmid_list.include? @vmid
@ -31,15 +29,11 @@ module Drivers
vmInfo = `VBoxManage showvminfo \"#{@vmid}\" --machinereadable`
@location = vmInfo.scan(/CfgFile=\"(.*?)\"/).flatten[0].to_s
@credentials = credentials
# TODO - Currently only implemented for the first set
if @credentials.count > 0
@vm_user = filter_input(@credentials[0]['user']) || "\'\'"
@vm_pass = filter_input(@credentials[0]['pass']) || "\'\'"
if !File.exist?(@location)
raise ArgumentError,"Couldn't find: " + @location
end
end
def register_and_return_vmid

View File

@ -5,13 +5,33 @@
#
# !!WARNING!! - All drivers are expected to filter input before running
# anything based on it. This is particularly important in the case
# of the drivers which wrap a command line program to provide
# functionality.
# of the drivers which wrap a command line to provide functionality.
#
module Lab
module Drivers
class VmDriver
attr_accessor :vmid
attr_accessor :location
attr_accessor :os
attr_accessor :tools
attr_accessor :credentials
def initialize(basic_driver_config)
@vmid = filter_command(basic_driver_config["vmid"])
@location = filter_command(basic_driver_config["location"])
@credentials = basic_driver_config["credentials"] || []
@tools = filter_input(basic_driver_config["tools"])
@os = filter_input(basic_driver_config["os"])
# Currently only implemented for the first set
if @credentials.count > 0
@vm_user = filter_input(@credentials[0]['user'])
@vm_pass = filter_input(@credentials[0]['pass'])
@vm_keyfile = filter_input(@credentials[0]['keyfile'])
end
end
def register # Must be implemented in a child *_driver class
raise "Command not Implemented"
@ -83,62 +103,33 @@ class VmDriver
private
# Currently this requires the net-ssh and net-scp gems. Ran into problems doing
# it with within metasploit (even though net-ssh is available). TODO - do something
# about this...
def scp_to(from,to)
gem 'net-ssh'
require 'net/ssh'
gem 'net-scp'
require 'net/scp'
#begin
# upload a file to a remote server
Net::SCP.start(@vmid, @vm_user, :password => @vm_pass) do |scp|
scp.upload!(from,to)
end
#rescue Exception => e
# return false
#end
end
def scp_from(from,to)
gem 'net-ssh'
require 'net/ssh'
gem 'net-scp'
require 'net/scp'
#begin
# download a file from a remote server
Net::SCP.start(@vmid, @vm_user, :password => @vm_pass) do |scp|
scp.download!(from,to)
end
#rescue Exception => e
# return false
#end
end
end
def ssh_exec(command)
gem 'net-ssh'
require 'net/ssh'
#begin
Net::SSH.start(@vmid, @vm_user, :password => @vm_pass) do |ssh|
result = ssh.exec!(command)
end
#rescue Exception => e
# return false
#end
end
def filter_input(string)
return "" unless string
return "" unless string # nil becomes empty string
return unless string.class == String # Allow other types unmodified
if !(string =~ /^[0-9\w\s\[\]\{\}\/\\\.\-\"\(\):!]*$/)
raise "WARNING! Invalid character in: #{string}"
end
@ -147,8 +138,9 @@ private
end
def filter_command(string)
return "" unless string
return "" unless string # nil becomes empty string
return unless string.class == String # Allow other types unmodified
if !(string =~ /^[0-9\w\s\[\]\{\}\/\\\.\-\"\(\)]*$/)
raise "WARNING! Invalid character in: #{string}"
end
@ -156,12 +148,11 @@ private
string
end
# The only reason we don't filter here is because we need
# the ability to still run clean (controlled entirely by us)
# command lines.
def system_command(command)
#begin
system(command)
#rescue Exception => e
# return false
# end
end
end

View File

@ -9,25 +9,11 @@ module Drivers
class WorkstationDriver < VmDriver
attr_accessor :type
attr_accessor :location
def initialize(vmid, location, os=nil, tools=false, credentials=nil)
@vmid = filter_command(vmid)
@location = filter_command(location)
def initialize(config)
super(config)
if !File.exist?(@location)
raise ArgumentError,"Couldn't find: " + @location
end
@credentials = credentials
@tools = tools # not used in command lines, no filter
@os = os # not used in command lines, no filter
# TODO - Currently only implemented for the first set
if @credentials.count > 0
@vm_user = filter_input(@credentials[0]['user']) || "\'\'"
@vm_pass = filter_input(@credentials[0]['pass']) || "\'\'"
raise ArgumentError,"Couldn't find: #{@location}"
end
end
@ -132,31 +118,49 @@ class WorkstationDriver < VmDriver
def copy_from(from, to)
from = filter_input(from)
to = filter_input(to)
vmrunstr = "vmrun -T ws -gu \'#{@vm_user}\' -gp \'#{@vm_pass}\' copyFileFromGuestToHost " +
"\'#{@location}\' \'#{from}\' \'#{to}\'"
if @tools
vmrunstr = "vmrun -T ws -gu \'#{@vm_user}\' -gp \'#{@vm_pass}\' copyFileFromGuestToHost " +
"\'#{@location}\' \'#{from}\' \'#{to}\'"
else
scp_from(from, to)
end
system_command(vmrunstr)
end
def copy_to(from, to)
from = filter_input(from)
to = filter_input(to)
vmrunstr = "vmrun -T ws -gu #{@vm_user} -gp #{@vm_pass} copyFileFromHostToGuest " +
"\'#{@location}\' \'#{from}\' \'#{to}\'"
system_command(vmrunstr)
if @tools
vmrunstr = "vmrun -T ws -gu #{@vm_user} -gp #{@vm_pass} copyFileFromHostToGuest " +
"\'#{@location}\' \'#{from}\' \'#{to}\'"
system_command(vmrunstr)
else
scp_to(from, to)
end
end
def check_file_exists(file)
file = filter_input(file)
vmrunstr = "vmrun -T ws -gu \'#{@vm_user}\' -gp \'#{@vm_pass}\' fileExistsInGuest " +
if @tools
vmrunstr = "vmrun -T ws -gu \'#{@vm_user}\' -gp \'#{@vm_pass}\' fileExistsInGuest " +
"\'#{@location}\' \'#{file}\' "
system_command(vmrunstr)
system_command(vmrunstr)
else
raise "Unsupported"
end
end
def create_directory(directory)
directory = filter_input(directory)
vmrunstr = "vmrun -T ws -gu \'#{@vm_user}\' -gp \'#{@vm_pass}\' createDirectoryInGuest " +
" \'#{@location}\' \'#{directory}\' "
system_command(vmrunstr)
if @tools
vmrunstr = "vmrun -T ws -gu \'#{@vm_user}\' -gp \'#{@vm_pass}\' createDirectoryInGuest " +
" \'#{@location}\' \'#{directory}\' "
system_command(vmrunstr)
else
raise "Unsupported"
end
end
def cleanup

View File

@ -1,10 +1,8 @@
require 'driver/workstation_driver'
require 'driver/workstation_vixr_driver'
require 'driver/remote_workstation_driver'
require 'driver/virtualbox_driver'
require 'driver/fog_driver'
require 'driver/dynagen_driver'
require 'driver/remote_workstation_driver'
require 'driver/remote_esx_driver'
#require 'driver/qemu_driver'
#require 'driver/qemudo_driver'
#require 'driver/amazon_driver'
#require 'driver/fog_driver'

View File

@ -0,0 +1,16 @@
module Lab
module Modifier
module Backtrack5
def nmap(options)
run_command("nmap #{filter_input(options)}")
end
def testssl(site)
run_command("/pentest/scanners/testssl/testssl.sh #{filter_input(site)}")
end
end
end
end

View File

@ -0,0 +1,131 @@
$:.unshift(File.join(File.dirname(__FILE__), '..', '..'))
module Lab
module Modifier
module Meterpreter
end
end
end
# This allows us to override the default way of running commands
# Currently useful for the esx controller
module Lab
class Vm
attr_accessor :framework
attr_accessor :session
attr_accessor :session_input
attr_accessor :session_output
def create_framework
return if @framework
@framework = Msf::Simple::Framework.create
end
# perform the setup only once
def setup_meterpreter
return if @session
# require the framework (assumes this sits in lib/lab/modifiers)
require 'msf/base'
create_framework
@session = nil
@session_input = Rex::Ui::Text::Input::Buffer.new
@session_output = Rex::Ui::Text::Output::Buffer.new
if @os == "windows"
exploit_name = 'windows/smb/psexec'
payload_name = 'windows/meterpreter/bind_tcp'
options = { "RHOST" => @vmid,
"SMBUser" => @vm_user,
"SMBPass" => @vm_pass}
# Initialize the exploit instance
exploit = @framework.exploits.create(exploit_name)
begin
# Fire it off.
@session = exploit.exploit_simple(
'Payload' => payload_name,
'Options' => options,
'LocalInput' => @session_input,
'LocalOutput' => @session_output)
@session.load_stdapi
rescue
raise "Unable to exploit"
end
else
module_name = 'scanner/ssh/ssh_login'
payload_name = 'linux/x86/meterpreter/bind_tcp'
options = { "RHOSTS" => @vmid,
"USERNAME" => @vm_user,
"PASSWORD" => @vm_pass,
"BLANK_PASSWORDS" => false,
"USER_AS_PASS" => false,
"VERBOSE" => false}
# Initialize the module instance
aux = @framework.auxiliary.create(module_name)
begin
# Fire it off.
aux.run_simple(
'Payload' => payload_name,
'Options' => options,
'LocalInput' => @session_input,
'LocalOutput' => @session_output)
@session = @framework.sessions.first.last
rescue
raise "Unable to exploit"
end
end
end
def run_command(command, timeout=60)
setup_meterpreter
@session.shell_command_token(command, timeout)
end
# This isn't part of the normal API, but too good to pass up.
def run_script(script, options)
if @session.type == "meterpreter"
@session.execute_script(script, options)
else
raise "Unsupported on #{@session.type}"
end
end
# For meterpreter API compatibility
#def execute_file(script,options)
# run_script(script,options)
#end
def copy_to(from,to)
setup_meterpreter
if @session.type == "meterpreter"
@session.run_cmd("upload #{from} #{to}")
else
@driver.copy_to(from,to)
end
end
def copy_from(from,to)
setup_meterpreter
if @session.type == "meterpreter"
@session.run_cmd("download #{from} #{to}")
else
@driver.copy_from(from,to)
end
end
end
end

View File

@ -4,6 +4,13 @@
module Lab
module Modifier
module Test
def dhclient
run_command("sudo dhclient")
end
def route
run_command("route -n")
end
def install_nmap
run_command("sudo apt-get install nmap")
@ -12,11 +19,6 @@ module Test
def nmap(options)
run_command("nmap #{filter_input(options)}")
end
def test
run_command("echo yo!")
end
end
end
end

View File

@ -1 +1,3 @@
require 'modifier/test_modifier'
require 'modifier/backtrack_modifier'
require 'modifier/meterpreter_modifier'

View File

@ -31,64 +31,70 @@ class Vm
def initialize(config = {})
# Mandatory
# TODO - This is such a mess. clean up, and pass stuff down to drivers
# and then rework the code that uses this api.
@vmid = config['vmid']
raise "Invalid VMID" unless @vmid
@driver = nil
@driver_type = filter_input(config['driver'])
@driver_type.downcase!
@name = config['name'] || "" # not used in command lines
@description = config['description'] || "" # not used in command lines
@tools = config['tools'] || false # don't filter this, not used in cmdlines
@os = config['os'] || nil
@arch = config['arch'] || nil
@location = filter_input(config['location'])
@name = config['name'] || ""
@description = config['description'] || ""
@tools = config['tools'] || ""
@os = config['os'] || ""
@arch = config['arch'] || ""
@type = filter_input(config['type']) || "unspecified"
@credentials = config['credentials'] || []
# Load in a list of modifiers. These provide additional methods
# TODO - currently it is up to the user to verify that
# modifiers are properly used with the correct VM image. If not,
# the results are likely to be disasterous.
@modifiers = config['modifiers']
# Optional for virtualbox
@location = filter_input(config['location'])
# TODO - Currently only implemented for the first set
if @credentials.count > 0
@vm_user = filter_input(@credentials[0]['user']) || "\'\'"
@vm_pass = filter_input(@credentials[0]['pass']) || "\'\'"
@vm_keyfile = filter_input(@credentials[0]['keyfile'])
end
# Only applicable to remote systems
@user = filter_input(config['user']) || nil
@host = filter_input(config['host']) || nil
# pass might need to be unfiltered, or filtered less
@pass = filter_input(config['pass']) || nil
#Only dynagen
#Only dynagen systems need this
@platform = config['platform']
#Only fog system need this
@fog_config = config['fog_config']
# Process the correct driver
if @driver_type == "workstation"
@driver = Lab::Drivers::WorkstationDriver.new(@vmid, @location, @os, @tools, @credentials)
elsif @driver_type == "workstation_vixr"
@driver = Lab::Drivers::WorkstationVixrDriver.new(@vmid, @location, @os, @tools, @user, @host, @credentials)
elsif @driver_type == "remote_workstation"
@driver = Lab::Drivers::RemoteWorkstationDriver.new(@vmid, @location, @os, @tools, @user, @host, @credentials)
@driver = Lab::Drivers::WorkstationDriver.new(config)
elsif @driver_type == "virtualbox"
@driver = Lab::Drivers::VirtualBoxDriver.new(@vmid, @location, @credentials)
@driver = Lab::Drivers::VirtualBoxDriver.new(config)
elsif @driver_type == "fog"
@driver = Lab::Drivers::FogDriver.new(config, config['fog_config'])
elsif @driver_type == "dynagen"
@driver = Lab::Drivers::DynagenDriver.new(@vmid, @location,@platform)
@driver = Lab::Drivers::DynagenDriver.new(config, config['dynagen_config'])
elsif @driver_type == "remote_esx"
@driver = Lab::Drivers::RemoteEsxDriver.new(@vmid, @os, @tools, @user, @host, @credentials)
@driver = Lab::Drivers::RemoteEsxDriver.new(config)
elsif @driver_type == "remote_workstation"
@driver = Lab::Drivers::RemoteWorkstationDriver.new(config)
#elsif @driver_type == "qemu"
# @driver = Lab::Drivers::QemuDriver.new
#elsif @driver_type == "qemudo"
# @driver = Lab::Drivers::QemudoDriver.new
#elsif @driver_type == "fog"
# @driver = Lab::Drivers::FogDriver.new
else
raise "Unknown Driver Type"
end
# Load in a list of modifiers. These provide additional methods
# Currently it is up to the user to verify that
# modifiers are properly used with the correct VM image.
#
# If not, the results are likely to be disasterous.
@modifiers = config['modifiers']
if @modifiers
@modifiers.each { |modifier| self.class.send(:include, eval("Lab::Modifier::#{modifier}"))}
end
@ -176,34 +182,48 @@ class Vm
end
def to_s
return "#{@vmid}: #{@location}"
return "#{@vmid}"
end
def to_yaml
# TODO - push this down to the drivers.
# Standard configuration options
out = " - vmid: #{@vmid}\n"
out += " driver: #{@driver_type}\n"
out += " location: #{@driver.location}\n"
out += " location: #{@location}\n"
out += " type: #{@type}\n"
out += " tools: #{@tools}\n"
out += " os: #{@os}\n"
out += " arch: #{@arch}\n"
if @user or @host # Remote vm/drivers only
out += " user: #{@user}\n"
out += " host: #{@host}\n"
end
if @platform
out += " platform: #{@platform}\n"
end
if @fog_config
out += @fog_config.to_yaml
end
out += " credentials:\n"
@credentials.each do |credential|
out += " - user: #{credential['user']}\n"
out += " pass: #{credential['pass']}\n"
end
return out
end
private
def filter_input(string)
return unless string
return "" unless string # nil becomes empty string
return unless string.class == String # Allow other types
if !(string =~ /^[(!)\d*\w*\s*\[\]\{\}\/\\\.\-\"\(\)]*$/)
raise "WARNING! Invalid character in: #{string}"

View File

@ -27,25 +27,20 @@ module Controllers
include Enumerable
include Lab::Controllers::WorkstationController
include Lab::Controllers::WorkstationVixrController
include Lab::Controllers::RemoteWorkstationController
include Lab::Controllers::VirtualBoxController
include Lab::Controllers::FogController
include Lab::Controllers::DynagenController
include Lab::Controllers::RemoteEsxController
include Lab::Controllers::RemoteWorkstationController
#include Lab::Controllers::QemuController
#include Lab::Controllers::QemudoController
#include Lab::Controllers::AmazonController
#include Lab::Controllers::FogController
def initialize (labdef=nil)
@vms = [] ## Start with an empty array of vms
## labdef is a big array of hashes, use yaml to store
labdef = [] unless labdef
## Create vm objects from the lab definition
load_vms(labdef)
# Start with an empty array of vm objects
@vms = []
# labdef is a just a big array of hashes
load_vms(labdef) if labdef
end
def clear!
@ -53,7 +48,12 @@ module Controllers
end
def [](x)
find_by_vmid(x)
# Support indexing by both names and number
if x.class == String
find_by_vmid(x)
else
return @vms[x]
end
end
def find_by_vmid(vmid)
@ -65,8 +65,7 @@ module Controllers
return nil
end
def add_vm(vmid, type,location,credentials=nil,user=nil,host=nil)
def add_vm(vmid, location=nil, os=nil, tools=nil, credentials=nil, user=nil, host=nil)
@vms << Vm.new( { 'vmid' => vmid,
'driver' => type,
'location' => location,
@ -80,20 +79,13 @@ module Controllers
end
def from_file(file)
labdef = YAML::load_file(file)
load_vms(labdef)
load_vms(YAML::load_file(file))
end
def load_vms(vms)
vms.each do |item|
begin
vm = Vm.new(item)
@vms << vm unless includes_vmid? vm.vmid
rescue Exception => e
# TODO - this needs to go into a logfile and be raised up to an interface.
puts "Invalid VM definition"
puts "Exception: #{e.to_s}"
end
vm = Vm.new(item)
@vms << vm unless includes_vmid? vm.vmid
end
end
@ -110,10 +102,10 @@ module Controllers
end
def includes_vmid?(vmid)
@vms.each do |vm|
@vms.each do |vm|
return true if (vm.vmid == vmid)
end
return false
false
end
def build_from_dir(driver_type, dir, clear=false)
@ -124,18 +116,16 @@ module Controllers
if driver_type.downcase == "workstation"
vm_list = ::Lab::Controllers::WorkstationController::dir_list(dir)
elsif driver_type.downcase == "workstation_vixr"
vm_list = ::Lab::Controllers::WorkstationVixrController::dir_list(dir)
elsif driver_type.downcase == "remote_workstation"
vm_list = ::Lab::Controllers::RemoteWorkstationController::dir_list(dir)
elsif driver_type.downcase == "virtualbox"
vm_list = ::Lab::Controllers::VirtualBoxController::dir_list(dir)
elsif driver_type.downcase == "fog"
vm_list = ::Lab::Controllers::FogController::dir_list(dir)
elsif driver_type.downcase == "Dynagen"
vm_list = ::Lab::Controllers::DynagenController::dir_list(dir)
elsif driver_type.downcase == "remote_workstation"
vm_list = ::Lab::Controllers::RemoteWorkstationController::dir_list(dir)
elsif driver_type.downcase == "remote_esx"
vm_list =::Lab::Controllers::RemoteEsxController::dir_list(dir)
#elsif driver_type.downcase == "esxi_vixr"
# vm_list =::Lab::Controllers::EsxiVixrController::dir_list(dir)
#elsif driver_type.downcase == "fog"
# vm_list = ::Lab::Controllers::FogController::dir_list(dir)
else
raise TypeError, "Unsupported VM Type"
end
@ -168,6 +158,19 @@ module Controllers
'host' => host } )
end
when :virtualbox
vm_list = ::Lab::Controllers::VirtualBoxController::running_list
vm_list.each do |item|
## Add it to the vm list
@vms << Vm.new( { 'vmid' => "#{item}",
'driver' => driver_type,
'location' => nil })
end
when :fog
raise "Unsupported" # TODO - figure out a way to allow this
when :dynagen
raise "Unsupported"
when :remote_workstation
vm_list = ::Lab::Controllers::RemoteWorkstationController::running_list(user, host)
@ -183,21 +186,6 @@ module Controllers
'user' => user,
'host' => host } )
end
when :virtualbox
vm_list = ::Lab::Controllers::VirtualBoxController::running_list
# TODO - why are user and host specified here?
vm_list.each do |item|
## Add it to the vm list
@vms << Vm.new( { 'vmid' => "#{item}",
'driver' => driver_type,
'location' => nil, # this will be filled in by the driver
'user' => user,
'host' => host } )
end
when :remote_esx
vm_list = ::Lab::Controllers::RemoteEsxController::running_list(user,host)
@ -216,7 +204,6 @@ module Controllers
end
def build_from_config(driver_type=nil, user=nil, host=nil, clear=false)
if clear
@vms = []
end