From 5172768cb95e716c71dc3d56bf4a5e3e0ad58d14 Mon Sep 17 00:00:00 2001 From: Jonathan Cran Date: Wed, 16 Feb 2011 02:15:24 +0000 Subject: [PATCH] Merging in VirtualBox control functionality from Hauke / Fidius Project git-svn-id: file:///home/svn/framework3/trunk@11753 4d416f70-5f16-0410-b530-b9f4589650da --- lib/lab/amazon_driver.rb | 25 +---- lib/lab/dynagen_driver.rb | 1 - lib/lab/remote_workstation_driver.rb | 23 +++-- lib/lab/virtualbox_controller.rb | 22 +++++ lib/lab/virtualbox_driver.rb | 135 +++++++++++++++++++-------- lib/lab/vm.rb | 32 ++++--- lib/lab/vm_controller.rb | 79 ++++++++++------ lib/lab/vm_driver.rb | 11 ++- lib/lab/workstation_driver.rb | 21 +++-- 9 files changed, 221 insertions(+), 128 deletions(-) create mode 100644 lib/lab/virtualbox_controller.rb diff --git a/lib/lab/amazon_driver.rb b/lib/lab/amazon_driver.rb index 87adb88b54..3de4f8cc4e 100644 --- a/lib/lab/amazon_driver.rb +++ b/lib/lab/amazon_driver.rb @@ -3,6 +3,7 @@ require 'vm_driver' ## ## $Id$ ## + module Lab module Drivers class AmazonDriver < VmDriver @@ -21,16 +22,7 @@ module Drivers @access_key = key @secret_access_key = secret_key @location = filter_input(location) - @type = "ec2" - end - - def filter_input(string) - - if !(string =~ /^[[:alnum:]\/\\\-\.\(\)\ _]*$/) - raise ArgumentError,"Invalid character in: #{string}" - end - - return string.gsub(/^[^[:alnum:]\/\\\-\.\(\)\ _]*$/, '') + @type = "amazon" end def register @@ -86,19 +78,6 @@ module Drivers end def running? - ## Get running Vms - running = `dynagen ?` #TODO - running_array = running.split("\n") - - ## Skip the first 4 lines of output - 4.times { running_array.shift } - - running_array.each do |vmx| - if vmx.to_s == @location.to_s - return true - end - end - return false end end diff --git a/lib/lab/dynagen_driver.rb b/lib/lab/dynagen_driver.rb index c1d4af702e..8e051369f1 100644 --- a/lib/lab/dynagen_driver.rb +++ b/lib/lab/dynagen_driver.rb @@ -88,7 +88,6 @@ module Drivers end def cleanup - `killall dynagen` `killall dynamips` @running = false diff --git a/lib/lab/remote_workstation_driver.rb b/lib/lab/remote_workstation_driver.rb index 609eb3bffa..011ea45782 100644 --- a/lib/lab/remote_workstation_driver.rb +++ b/lib/lab/remote_workstation_driver.rb @@ -14,20 +14,16 @@ class RemoteWorkstationDriver < VmDriver def initialize(location, user=nil, host=nil, credentials=nil) - ## Can we check file existence? - - #if !File.exist?(location) - # raise ArgumentError,"Couldn't find: " + location - #end + ## 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 @location = filter_input(location) - @host = filter_input(host) @user = filter_input(user) + @host = filter_input(host) @credentials = filter_input_credentials(credentials) - @type = "RemoteWorkstation" + @type = "remote_workstation" end def start @@ -51,14 +47,17 @@ class RemoteWorkstationDriver < VmDriver end def create_snapshot(snapshot) + snapshot = filter_input(snapshot) system_command("ssh #{@user}@#{@host} vmrun -T ws snapshot \\\'#{@location}\\\' #{snapshot} nogui") end def revert_snapshot(snapshot) + snapshot = filter_input(snapshot) system_command("ssh #{@user}@#{@host} vmrun -T ws revertToSnapshot \\\'#{@location}\\\' #{snapshot} nogui") end def delete_snapshot(snapshot) + snapshot = filter_input(snapshot) system_command("ssh #{@user}@#{@host} vmrun -T ws deleteSnapshot \\\'#{@location}\\\' #{snapshot} nogui" ) end @@ -69,7 +68,7 @@ class RemoteWorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass'] @@ -87,7 +86,7 @@ class RemoteWorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass'] @@ -104,7 +103,7 @@ class RemoteWorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass'] @@ -121,7 +120,7 @@ class RemoteWorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass'] @@ -138,7 +137,7 @@ class RemoteWorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass'] diff --git a/lib/lab/virtualbox_controller.rb b/lib/lab/virtualbox_controller.rb new file mode 100644 index 0000000000..c2874c8c53 --- /dev/null +++ b/lib/lab/virtualbox_controller.rb @@ -0,0 +1,22 @@ +module Lab +module Controllers +module VirtualBoxController + + def self.running_list + vm_names_and_uuids = `VBoxManage list runningvms`.split("\n") + 4.times { vm_names_and_uuids.shift } + + vm_names = [] + vm_names_and_uuids.each do |entry| + vm_names << entry.split('"')[1] + end + + return vm_names + end + + def self.dir_list(basepath=nil) + vm_list = Find.find(basepath).select { |f| f =~ /\.xml$/ } + end +end +end +end diff --git a/lib/lab/virtualbox_driver.rb b/lib/lab/virtualbox_driver.rb index 18ac662520..c36917fa6c 100644 --- a/lib/lab/virtualbox_driver.rb +++ b/lib/lab/virtualbox_driver.rb @@ -1,4 +1,5 @@ require 'vm_driver' +require 'nokogiri' ## ## $Id$ @@ -10,105 +11,157 @@ module Drivers attr_accessor :type attr_accessor :location - def initialize(location) - if !File.exist?(location) - raise ArgumentError,"Couldn't find: " + location - end + def initialize(vmid, location=nil) + @vmid = filter_input(vmid) @location = filter_input(location) - @type = "vbox" - @name = "" - - register + @type = "virtualbox" + + ## Check to see if we already know this vm, if not, go on location + vmid_list = get_vm_names + unless vmid_list.include? @vmid + raise "Error, no such vm: #{@vmid}" unless @location + + if !File.exist?(@location) + raise ArgumentError,"Error, no vm at: #{@location}" + end + + puts "Registering #{@location}" + @vmid = register_and_return_vmid + end + + vmInfo = `VBoxManage showvminfo \"#{@vmid}\" --machinereadable` + @location = vmInfo.scan(/CfgFile=\"(.*?)\"/).flatten.to_s end - def register - name_string = `VBoxManage registervm #{@location}` - ##TODO - parse out name / uuid + def register_and_return_vmid + + xml = Nokogiri::XML(File.new(@location)) + vmid = xml.root.xpath("//Machine[@name]") + + ## only register if we don't already know the vmid + if !get_vm_names.include? vmid + system_command("VBoxManage registervm \"#{@location}\"") + end + + return vmid + end def unregister - system_command("VBoxManage unregistervm #{@name}") + system_command("VBoxManage unregistervm \"#{@vmid}\"") end def start - system_command("VBoxManage startvm #{@name}") + system_command("VBoxManage startvm \"#{@vmid}\"") end def stop - system_command("VBoxManage controlvm#{@name} poweroff") + system_command("VBoxManage controlvm \"#{@vmid}\" poweroff") end def suspend - system_command("VBoxManage controlvm #{@name} pause") + system_command("VBoxManage controlvm \"#{@vmid}\" savestate") end def pause - system_command("VBoxManage controlvm #{@name} pause") + system_command("VBoxManage controlvm \"#{@vmid}\" pause") end def reset - system_command("VBoxManage controlvm #{@name} reset") + system_command("VBoxManage controlvm \"#{@vmid}\" reset") end - def create_snapshot(name) - system_command("VBoxManage snapshot #{@name} take " + name) + def create_snapshot(snapshot) + snapshot = filter_input(snapshot) + system_command("VBoxManage snapshot \"#{@vmid}\" take " + snapshot) end - def revert_snapshot(name) - system_command("VBoxManage snapshot #{@name} restore " + name) + def revert_snapshot(snapshot) + snapshot = filter_input(snapshot) + system_command("VBoxManage snapshot \"#{@vmid}\" restore " + snapshot) end - def delete_snapshot(name) - system_command("VBoxManage snapshot #{@name} delete " + name ) + def delete_snapshot(snapshot) + snapshot = filter_input(snapshot) + system_command("VBoxManage snapshot \"#{@vmid}\" delete " + snapshot) end def run_command(command, arguments, user, pass) - command = "VBoxManage execute #{@name} #{command} --username #{username} - --password #{password} --arguments \"#{arguments}\"" + command = "VBoxManage guestcontrol exec \"#{@vmid}\" \"#{command}\" --username \"#{user}\" + --password \"#{pass}\" --arguments \"#{arguments}\"" system_command(command) end def copy_from(user, pass, from, to) - command = "VBoxManage " ##TODO - system_command(command) + raise "Not supported by Virtual Box" end def copy_to(user, pass, from, to) - command = "VBoxManage " ##TODO + command = "VBoxManage guestcontrol copyto \"#{@vmid}\" \"#{from}\" \"#{to}\" + --username \"#{user}\" --password \"#{pass}\"" system_command(command) end def check_file_exists(user, pass, file) - command = "VBoxManage " ##TODO - system_command(command) + raise "Not supported by Virtual Box" end def create_directory(user, pass, directory) - command = "VBoxManage " ##TODO + command = "VBoxManage guestcontrol createdir \"#{@vmid}\" \"#{directory}\" + --username \"#{user}\" --password \"#{pass}\"" system_command(command) end def cleanup - self.unregister + end def running? ## Get running Vms - running = `VBoxManage list runningvms` - running_array = running.split("\n") + get_running_vm_names.include? @vmid + end + + private + + def get_vm_names + ## Get Known VMs + vm_names_and_uuids = `VBoxManage list vms`.split("\n") + 4.times { vm_names_and_uuids.shift } - ## Skip the first 4 lines of output - 4.times { running_array.shift } + vm_names = [] + vm_names_and_uuids.each do |entry| + vm_names << entry.split('"')[1] + end + + return vm_names + end - running_array.each do |vmx| - if vmx.to_s == @location.to_s - return true - end + def get_vm_uuids + ## Get Known VMs + vm_names_and_uuids = `VBoxManage list vms`.split("\n") + 4.times { vm_names_and_uuids.shift } + + vm_uuids = [] + vm_names_and_uuids.each do |entry| + vm_names << entry.split('"')[2] end + + return vm_uuids + end + + def get_running_vm_names + ## Get Known VMs + vm_names_and_uuids = `VBoxManage list runningvms`.split("\n") + 4.times { vm_names_and_uuids.shift } - return false + vm_names = [] + vm_names_and_uuids.each do |entry| + vm_names << entry.split('"')[1] + end + + return vm_names end end end diff --git a/lib/lab/vm.rb b/lib/lab/vm.rb index acc83015b6..c07fe07d37 100644 --- a/lib/lab/vm.rb +++ b/lib/lab/vm.rb @@ -5,7 +5,7 @@ require 'workstation_driver' require 'remote_workstation_driver' #require 'dynagen_driver' -#require 'virtualbox_driver' +require 'virtualbox_driver' #require 'amazon_driver' module Lab @@ -14,7 +14,6 @@ class Vm attr_accessor :vmid attr_accessor :driver - attr_accessor :location attr_accessor :credentials attr_accessor :tools attr_accessor :type @@ -27,15 +26,18 @@ class Vm ## location (file / uri) ## credentials (of the form [ {'user'=>"user",'pass'=>"pass", 'admin' => false}, ... ]) def initialize(config = {}) + + ## Mandatory + @vmid = config['vmid'] + raise Exception, "Invalid VMID" unless @vmid + @driver = nil driver_type = config['driver'] driver_type.downcase! - ## Mandatory - @vmid = config['vmid'] - @location = config['location'] ## Optional + @location = config['location'] ## only optional in the case of virtualbox (currently) @type = config['type'] || "unspecified" @tools = config['tools'] || false ## TODO @credentials = config['credentials'] || [] @@ -43,7 +45,7 @@ class Vm @ports = nil ## TODO @vulns = nil ## TODO - ## Remote + ## Only applicable to remote systems @user = config['user'] || nil @host = config['host'] || nil @@ -51,12 +53,12 @@ class Vm @driver = Lab::Drivers::WorkstationDriver.new(@location, @credentials) elsif driver_type == "remote_workstation" @driver = Lab::Drivers::RemoteWorkstationDriver.new(@location, @user, @host, @credentials) - elsif driver_type == "dynagen" - @driver = Lab::Drivers::DynagenDriver.new + #elsif driver_type == "dynagen" + # @driver = Lab::Drivers::DynagenDriver.new elsif driver_type == "virtualbox" - @driver = Lab::Drivers::VirtualBoxDriver.new - elsif driver_type == "amazon" - @driver = Lab::Drivers::AmazonDriver.new + @driver = Lab::Drivers::VirtualBoxDriver.new(@vmid, @location) + #elsif driver_type == "amazon" + # @driver = Lab::Drivers::AmazonDriver.new else raise Exception, "Unknown Driver Type" end @@ -67,6 +69,10 @@ class Vm @driver.running? end + def location + @driver.location + end + def start @driver.start end @@ -131,8 +137,8 @@ class Vm def to_yaml out = " - vmid: #{@vmid}\n" out += " driver: #{@driver.type}\n" - out += " location: #{@location}\n" - out = " type: #{@type}\n" + out += " location: #{@driver.location}\n" + out += " type: #{@type}\n" out += " tools: #{@tools}\n" out += " credentials:\n" @credentials.each do |credential| diff --git a/lib/lab/vm_controller.rb b/lib/lab/vm_controller.rb index da52c5a658..03905e81e9 100644 --- a/lib/lab/vm_controller.rb +++ b/lib/lab/vm_controller.rb @@ -15,7 +15,7 @@ require 'workstation_controller' require 'remote_workstation_controller' #require 'qemu_controller' #require 'amazon_controller' -#require 'virtualbox_controller' +require 'virtualbox_controller' #require 'dynagen_controller' module Lab @@ -27,7 +27,7 @@ module Controllers include Lab::Controllers::RemoteWorkstationController ## gives access to workstation-specific controller methods #include Lab::Controllers::QemuController ## gives access to qemu-specific controller methods #include Lab::Controllers::AmazonController ## gives access to amazon-specific controller methods - #include Lab::Controllers::VirtualBoxController ## gives access to virtualbox-specific controller methods + include Lab::Controllers::VirtualBoxController ## gives access to virtualbox-specific controller methods #include Lab::Controllers::DynagenController ## gives access to dynagen-specific controller methods @@ -47,7 +47,7 @@ module Controllers begin @vms << Vm.new(item) rescue Exception => e - puts e.to_s + puts "Invalid VM definition" end end @@ -110,7 +110,7 @@ module Controllers @vms.each { |vm| if (vm.vmid.to_s == vmid.to_s) then return true end } end - def build_from_dir(dir, type, clear=false) + def build_from_dir(type, dir, clear=false) if clear @vms = [] @@ -120,13 +120,15 @@ module Controllers vm_list = ::Lab::Controllers::WorkstationController::dir_list(dir) elsif type.downcase == "remote_workstation" vm_list = ::Lab::Controllers::RemoteWorkstationController::dir_list(dir) + elsif type.downcase == "virtualbox" + vm_list = ::Lab::Controllers::VirtualBoxController::dir_list(dir) else raise TypeError, "Unsupported VM Type" end - vm_list.each do |item| - index = @vms.count + 1 - @vms << Vm.new( {"vmid" => index, "driver" => type, "location" => item} ) + vm_list.each_index do |index| + puts "Creating VM object for: " + vm_list[index] + @vms << Vm.new( {'vmid' => index.to_s, 'driver' => type, 'location' => vm_list[index]} ) end end @@ -137,28 +139,53 @@ module Controllers end case type.intern - when :workstation - vm_list = ::Lab::Controllers::WorkstationController::running_list - when :remote_workstation - vm_list = ::Lab::Controllers::RemoteWorkstationController::running_list(user, host) - else - raise TypeError, "Unsupported VM Type" - end - - - vm_list.each do |item| + when :workstation + vm_list = ::Lab::Controllers::WorkstationController::running_list + + vm_list.each do |item| - ## Name the VM - index = @vms.count + 1 + ## Name the VM + index = @vms.count + 1 - ## Add it to the vm list - @vms << Vm.new( { 'vmid' => index, - 'driver' => type, - 'location' => item, - 'user' => user, - 'host' => host } ) - end + ## Add it to the vm list + @vms << Vm.new( { 'vmid' => index.to_s, + 'driver' => type, + 'location' => item, + 'user' => user, + 'host' => host } ) + end + + when :remote_workstation + vm_list = ::Lab::Controllers::RemoteWorkstationController::running_list(user, host) + + vm_list.each do |item| + ## Name the VM + index = @vms.count + 1 + + ## Add it to the vm list + @vms << Vm.new( { 'vmid' => "#{index}", + 'driver' => type, + 'location' => item, + 'user' => user, + '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' => type, + 'location' => nil, + 'user' => user, + 'host' => host } ) + end + + else + raise TypeError, "Unsupported VM Type" + end end diff --git a/lib/lab/vm_driver.rb b/lib/lab/vm_driver.rb index 57583a4744..640c5103c9 100644 --- a/lib/lab/vm_driver.rb +++ b/lib/lab/vm_driver.rb @@ -86,15 +86,20 @@ class VmDriver private def filter_input(string) - - if !(string =~ /^[[:alnum:]\/\\\-\.\(\)\ _]*$/) + return unless string + + if !(string =~ /^[\w\s\[\]\{\}\/\\\.\-\"\(\)]*$/) raise Exception, "Invalid character in: #{string}" end - return string.gsub(/^[^[:alnum:]\/\\\-\.\(\)\ _]*$/, '') + return string + + return string.gsub(/^[\w\s\[\]\{\}\/\\\.\-\"\(\)]*$/, "Invalid String") end def filter_input_credentials(credentials) + return unless credentials + credentials.each { |credential| credential['user'] = filter_input(credential['user']) credential['pass'] = filter_input(credential['pass']) diff --git a/lib/lab/workstation_driver.rb b/lib/lab/workstation_driver.rb index b6c5c79958..7cca1ac892 100644 --- a/lib/lab/workstation_driver.rb +++ b/lib/lab/workstation_driver.rb @@ -21,7 +21,7 @@ class WorkstationDriver < VmDriver @credentials = filter_input_credentials(credentials) - @type = "Workstation" + @type = "workstation" end @@ -46,15 +46,18 @@ class WorkstationDriver < VmDriver end def create_snapshot(snapshot) - system_command("vmrun -T ws snapshot " + "\"#{@location}\" \"#{@snapshot}\"") + snapshot = filter_input(snapshot) + system_command("vmrun -T ws snapshot " + "\"#{@location}\" \"#{snapshot}\"") end def revert_snapshot(snapshot) - system_command("vmrun -T ws revertToSnapshot " + "\"#{@location}\" \"#{@snapshot}\"") + snapshot = filter_input(snapshot) + system_command("vmrun -T ws revertToSnapshot " + "\"#{@location}\" \"#{snapshot}\"") end def delete_snapshot(snapshot) - system_command("vmrun -T ws deleteSnapshot " + "\"#{@location}\" \"#{@snapshot}\"" ) + snapshot = filter_input(snapshot) + system_command("vmrun -T ws deleteSnapshot " + "\"#{@location}\" \"#{snapshot}\"" ) end def run_command(command, named_user=nil) @@ -63,7 +66,7 @@ class WorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass'] @@ -81,7 +84,7 @@ class WorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass'] @@ -98,7 +101,7 @@ class WorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass'] @@ -115,7 +118,7 @@ class WorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass'] @@ -132,7 +135,7 @@ class WorkstationDriver < VmDriver ## this will return the first user if named_user doesn't exist ## -- that may not be entirely obvious... - cred = get_best_credentials(named_user) + cred = get_best_creds(named_user) user = cred['user'] pass = cred['pass']