From 43846189507c22cbceb2fde2c783c6d06f9a8871 Mon Sep 17 00:00:00 2001 From: Jonathan Cran Date: Mon, 17 Jan 2011 05:58:16 +0000 Subject: [PATCH] add workstation-over-ssh control into the lab vm-control plugin git-svn-id: file:///home/svn/framework3/trunk@11584 4d416f70-5f16-0410-b530-b9f4589650da --- lib/lab/remote_workstation_controller.rb | 19 +++ lib/lab/remote_workstation_driver.rb | 162 ++++++++++++++++++ lib/lab/vm.rb | 51 +++++- lib/lab/vm_controller.rb | 206 +++++++++++++---------- lib/lab/vm_driver.rb | 64 ++++++- lib/lab/workstation_driver.rb | 102 ++++++++--- 6 files changed, 477 insertions(+), 127 deletions(-) create mode 100644 lib/lab/remote_workstation_controller.rb create mode 100644 lib/lab/remote_workstation_driver.rb diff --git a/lib/lab/remote_workstation_controller.rb b/lib/lab/remote_workstation_controller.rb new file mode 100644 index 0000000000..4fd49361e0 --- /dev/null +++ b/lib/lab/remote_workstation_controller.rb @@ -0,0 +1,19 @@ +module Lab +module Controllers +module RemoteWorkstationController + + def workstation_running_list(user,host) + vm_list = `ssh #{user}@#{host} vmrun list nogui`.split("\n") + vm_list.shift + + return vm_list + end + + def workstation_dir_list(basepath=nil) + vm_list = Find.find(basepath).select { |f| f =~ /\.vmx$/ } + + return vm_list + end +end +end +end diff --git a/lib/lab/remote_workstation_driver.rb b/lib/lab/remote_workstation_driver.rb new file mode 100644 index 0000000000..d7edec873e --- /dev/null +++ b/lib/lab/remote_workstation_driver.rb @@ -0,0 +1,162 @@ +require 'vm_driver' + +## +## $Id$ +## + +module Lab +module Drivers + +class RemoteWorkstationDriver < VmDriver + + attr_accessor :type + attr_accessor :location + + 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 + + unless user then raise ArgumentError, "Must provide a username" end + unless host then raise ArgumentError, "Must provide a hostname" end + + @location = location + @host = host + @user = user + @credentials = credentials + @type = "RemoteWorkstation" + end + + def start + system_command("ssh #{@user}@#{@host} vmrun -T ws start \\\'#{@location}\\\' nogui") + end + + def stop + system_command("ssh #{@user}@#{@host} vmrun -T ws stop \\\'#{@location}\\\' nogui") + end + + def suspend + system_command("ssh #{@user}@#{@host} vmrun -T ws suspend \\\'#{@location}\\\' nogui") + end + + def pause + system_command("ssh #{@user}@#{@host} vmrun -T ws pause \\\'#{@location}\\\' nogui") + end + + def reset + system_command("ssh #{@user}@#{@host} vmrun -T ws reset \\\'#{@location}\\\' nogui") + end + + def create(snapshot) + system_command("ssh #{@user}@#{@host} vmrun -T ws snapshot \\\'#{@location}\\\' #{snapshot} nogui") + end + + def revert(snapshot) + system_command("ssh #{@user}@#{@host} vmrun -T ws revertToSnapshot \\\'#{@location}\\\' #{snapshot} nogui") + end + + def delete_snapshot(snapshot) + system_command("ssh #{@user}@#{@host} vmrun -T ws deleteSnapshot \\\'#{@location}\\\' #{snapshot} nogui" ) + end + + + def run_command(command, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + vmrunstr = "ssh #{@user}@#{@host} vmrun -T ws -gu \\\'{user}\\\' -gp \\\'{pass}\\\' runProgramInGuest \\\'#{@location}\\\' \\\'{command}\\\' -noWait -activeWindow nogui" + + system_command(vmrunstr) + end + + def copy_from(from, to, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + vmrunstr = "ssh #{@user}@#{@host} vmrun -T ws -gu {user} -gp {pass} copyFileFromGuestToHost \\\'#{@location}\\\' \\\'{from}\\\' \\\'{to}\\\' nogui" + system_command(vmrunstr) + end + + def copy_to(from, to, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + + vmrunstr = "ssh #{@user}@#{@host} vmrun -T ws -gu {user} -gp {pass} copyFileFromHostToGuest \\\'#{@location}\\\' \\\'{from}\\\' \\\'{to}\\\' nogui" + system_command(vmrunstr) + end + + def check_file_exists(file, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + + vmrunstr = "ssh #{@user}@#{@host} vmrun -T ws -gu {user} -gp {pass} fileExistsInGuest \\\'#{@location}\\\' \\\'{file}\\\' nogui" + system_command(vmrunstr) + end + + def create_directory(directory, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + vmrunstr = "ssh #{@user}@#{@host} vmrun -T ws -gu {user} -gp {pass} createDirectoryInGuest \\\'#{@location}\\\' \\\'#{directory}\\\' nogui" + system_command(vmrunstr) + end + + + def cleanup + + end + + def running? + ## Get running Vms + running = `ssh #{@user}@#{@host} vmrun list nogui` + running_array = running.split("\n") + running_array.shift + + running_array.each do |vmx| + if vmx.to_s == @location.to_s + return true + end + end + + return false + end + +end + +end +end diff --git a/lib/lab/vm.rb b/lib/lab/vm.rb index ff2add3768..4d9098efdb 100644 --- a/lib/lab/vm.rb +++ b/lib/lab/vm.rb @@ -3,12 +3,15 @@ ## require 'workstation_driver' +require 'remote_workstation_driver' #require 'server_driver' #require 'qemu_driver' -#require 'vbox_driver' +#require 'virtualbox_driver' #require 'ec2_driver' #require 'azure_driver' +module Lab + class Vm attr_accessor :vmid @@ -18,10 +21,12 @@ class Vm attr_accessor :tools ## Initialize takes a vm configuration hash of the form - ## - driver (vm technology) ## - vmid (unique identifier) - ## - location (file / uri) - ## - credentials (of the form [ {'user'=>"user",'pass'=>"pass", 'admin' => false}, ... ]) + ## driver (vm technology) + ## user (if applicable) + ## host (if applicable) + ## location (file / uri) + ## credentials (of the form [ {'user'=>"user",'pass'=>"pass", 'admin' => false}, ... ]) def initialize(config = {}) @driver = nil driver_type = config['driver'] @@ -37,8 +42,13 @@ class Vm @ports = nil ## TODO @vulns = nil ## TODO + @user = config['user'] || nil + @host = config['host'] || nil + if driver_type == "workstation" - @driver = WorkstationDriver.new(@location) + @driver = Lab::Drivers::WorkstationDriver.new(@location, @credentials) + elsif driver_type == "remote_workstation" + @driver = Lab::Drivers::RemoteWorkstationDriver.new(@location,@user, @host, @credentials) #elsif driver_type == "server" # @driver = ServerDriver.new #elsif driver_type == "virtualbox" @@ -75,6 +85,10 @@ class Vm @driver.suspend end + def reset + @driver.reset + end + def resume @driver.resume end @@ -83,8 +97,20 @@ class Vm @driver.snapshot(name) end + ## revert needs to restore the current state of the machine + ## meaning, if it's running when revert is called + ## we'll want to restart it after reverting it. def revert(name) + start = false + if running? + start = true + end + @driver.revert(name) + + if start + @driver.start + end end def copy_to(from_file,to_file) @@ -108,17 +134,24 @@ class Vm end def to_yaml - out = " - vmid: #{vmid}\n" - out += " driver: #{driver.type}\n" - out += " location: #{location}\n" - out += " tools: #{tools}\n" + out = " - vmid: #{@vmid}\n" + out += " driver: #{@driver.type}\n" + out += " location: #{@location}\n" + out += " tools: #{@tools}\n" out += " credentials:\n" @credentials.each do |credential| out += " - user: #{credential['user']}\n" out += " pass: #{credential['pass']}\n" out += " admin: #{credential['admin']}\n" end + + if @server_user or @server_host + out += " server_user: #{@server_user}\n" + out += " server_host: #{@server_host}\n" + end return out end end + +end diff --git a/lib/lab/vm_controller.rb b/lib/lab/vm_controller.rb index f77a2ccc3b..fff30bd144 100644 --- a/lib/lab/vm_controller.rb +++ b/lib/lab/vm_controller.rb @@ -1,11 +1,8 @@ ## ## $Id$ ## -## This is the main lab controller which will call out to other controller -## libraries. Requiring this file and specifying the type of VM at initialization -## will allow you to start/stop/snapshot/revert & run commands on VMs +## This is the main lab controller. Require this file to create a lab of vms ## -## $Revision$ ## $:.unshift(File.expand_path(File.dirname(__FILE__))) ## Msf Test libraries @@ -14,112 +11,149 @@ require 'find' require 'enumerator' require 'vm' require 'yaml' +require 'workstation_controller' +require 'remote_workstation_controller' -# -# ~Higher-level lab methods which are generic to the types of things we want to do with a lab of machines -# Note that any generic vm functionality should be pushed down into the controller class. +#require 'amazon_controller' +#require 'virtualbox_controller' +#require 'dynagen_controller' -class VmController +module Lab +module Controllers + class VmController - include Enumerable + include Enumerable + include Lab::Controllers::WorkstationController ## gives access to workstation-specific controller methods + include Lab::Controllers::RemoteWorkstationController ## gives access to workstation-specific controller methods - def initialize (labdef = nil) +# include Lab::AmazonController ## gives access to amazon-specific controller methods +# include Lab::VirtualBoxController ## gives access to virtualbox-specific controller methods +# include Lab::DynagenController ## gives access to dynagen-specific controller methods + + + def initialize (labdef = nil) - @vms = [] ## Start with an empty array of vms + @vms = [] ## Start with an empty array of vms + + ## labdef is a big array of hashes (vms) - generally loaded from yaml + if !labdef + labdef = [] ## Just use a blank lab to start + else + labdef = labdef + end + + ## Create vm objects from the lab definition + labdef.each do |item| + begin + @vms << Vm.new(item) + rescue Exception => e + puts e.to_s + end + end - ## labdef is a big array of hashes (vms) - generally loaded from yaml - if !labdef - labdef = [] ## Just use a blank lab to start - else - labdef = labdef end - - ## Create vm objects from the lab definition - labdef.each do |item| - begin - @vms << Vm.new(item) - rescue Exception => e - puts e.to_s - end - end - - end - def clear! - @vms = [] - end + def clear! + @vms = [] + end - def find_by_vmid(vmid) - @vms.each do |vm| + def find_by_vmid(vmid) + @vms.each do |vm| - if (vm.vmid.to_s == vmid.to_s) - return vm + if (vm.vmid.to_s == vmid.to_s) + return vm + end + end + return nil + end + + def from_file(file) + labdef = YAML::load_file(file) + + labdef.each do |item| + #puts "Lab item: " + item.inspect + @vms << Vm.new(item) end end - return nil - end - def from_file(file) - labdef = YAML::load_file(file) - - labdef.each do |item| - puts "Lab item: " + item.inspect - @vms << Vm.new(item) + def to_file(file) + File.open(file, 'w') do |f| + @vms.each { |vm| f.puts vm.to_yaml } + end end - end - def to_file(file) - File.open(file, 'w') do |f| - @vms.each { |vm| f.puts vm.to_yaml } + def each + @vms.each { |vm| yield vm } end - end - def each - @vms.each { |vm| yield vm } - end - -# def includes?(vm) -# @vms.each { |vm| if (vm.vmid.to_ == vmid.to_s) then return true end } -# end - - def includes_vmid?(vmid) - @vms.each { |vm| if (vm.vmid.to_s == vmid.to_s) then return true end } - end - - def running?(vmid) - if exists?(vmid) - return self.find_by_vmid(vmid).running? + def includes?(specified_vm) + @vms.each { |vm| if (vm == specified_vm) then return true end } end - return false - end - ## Might want to mix this (workstation) functionality in? - def build_from_running_workstation(clear=false) - - if clear - @vms = [] + def includes_vmid?(vmid) + @vms.each { |vm| if (vm.vmid.to_s == vmid.to_s) then return true end } end + + def build_from_dir(dir, type, clear=false) - vm_list = `vmrun list`.split("\n") - vm_list.shift - vm_list.each do |vmx| - index = @vms.count + 1 ## give us a vmid! - @vms << Vm.new( {"vmid" => index, "driver" => "workstation", - "location" => vmx}) - end - end + if clear + @vms = [] + end - def build_from_dir_workstation(basepath=nil, clear=false) - - if clear - @vms = [] + if type.downcase == "workstation" + vm_list = WorkstationController::workstation_dir_list(dir) + elsif type.downcase == "remote_workstation" + vm_list = RemoteWorkstationController::workstation_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} ) + end end - vm_list = Find.find(basepath).select { |f| f =~ /\.vmx$/ } - vm_list.each do |vmx| - index = @vms.count + 1 ## give us a vmid! - @vms << Vm.new( {"vmid" => index, "driver" => "workstation", - "location" => vmx}) + def build_from_running(type, user=nil, host=nil, clear=false) + + if clear + @vms = [] + end + + if type.downcase == "workstation" + vm_list = WorkstationController::workstation_running_list + elsif type.downcase == "remote_workstation" + vm_list = RemoteWorkstationController::workstation_running_list(user, host) + 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} ) + end + end + + def add_vm(vmid, type,location,credentials=nil,user=nil,host=nil) + @vms << Vm.new( { "vmid" => vmid, + "driver" => type, + "location" => location, + "credentials" => credentials, + "user" => user, + "host" => host + } ) + end + + def remove_by_vmid(vmid) + @vms.delete(self.find_by_vmid(vmid)) + end + + def running?(vmid) + if exists?(vmid) + return self.find_by_vmid(vmid).running? + end + return false end end end +end diff --git a/lib/lab/vm_driver.rb b/lib/lab/vm_driver.rb index c6183d508e..29a5fa830d 100644 --- a/lib/lab/vm_driver.rb +++ b/lib/lab/vm_driver.rb @@ -1,13 +1,27 @@ ## ## $Id$ ## - +module Lab +module Drivers class VmDriver attr_accessor :type attr_accessor :location def initialize(location) + + @location = location + @host = host + @user = user + @credentials = credentials + @type = "" + + end + + def register + end + + def unregister end def start @@ -34,41 +48,75 @@ class VmDriver def delete_snapshot(snapshot) end - def run_command(command, user, pass) + def run_command(command, named_user=nil) end - def copy_from(user, pass, from, to) + def copy_from(from, to, named_user=nil) end - def copy_to(user, pass, from, to) + def copy_to(from, to, named_user=nil) end - def check_file_exists(user, pass, file) + def check_file_exists(file, named_user=nil) end - def create_directory(user, pass, directory) + def create_directory(directory, named_user=nil) end +=begin def ssh_exec(host, command, user) ssh_command = "ssh " + user + "@" + host + " " + command system_command(ssh_command) end def scp_from(host, user, from, to) - vmrunstr = "scp -r \"" + user + "@" + host + ":" + from + "\" \"" + to + "\"" ## TODO - setup keys + vmrunstr = "scp -r \"" + user + "@" + host + ":" + from + "\" \"" + to + "\"" system_command(vmrunstr) end def scp_to(host, user, from, to) - vmrunstr = "scp -r \"" + from + "\" \"" + user + "@" + host + ":" + to + "\"" ## TODO - setup keys + vmrunstr = "scp -r \"" + from + "\" \"" + user + "@" + host + ":" + to + "\"" system_command(vmrunstr) end +=end + + def cleanup + end private + + + ## Takes a username in the form of a string + ## and returns a credentials hash + def get_best_creds(named_user) + if !@credentials.empty? + return get_named_user_creds(named_user) || @credentials[0] + else + raise Exception, "No credentials for this VM ):" + end + end + + + ## Checks the array of credentials to see if we have one + ## with this user's username. returns the first. + def get_named_user_creds(user) + cretdentials.each do |credential| + if credential['user'].downcase == user.downcase + return credential + end + end + return nil + end def system_command(command) ## TODO - filter here + + puts "DEBUG: running system command: #{command}" + system(command) end end + +end +end diff --git a/lib/lab/workstation_driver.rb b/lib/lab/workstation_driver.rb index 38870e4361..dfac6e6a0c 100644 --- a/lib/lab/workstation_driver.rb +++ b/lib/lab/workstation_driver.rb @@ -4,83 +4,134 @@ require 'vm_driver' ## $Id$ ## +module Lab +module Drivers + class WorkstationDriver < VmDriver attr_accessor :type attr_accessor :location - def initialize(location) + def initialize(location, credentials=nil) if !File.exist?(location) raise ArgumentError,"Couldn't find: " + location end @location = location + @credentials = credentials @type = "Workstation" + end def start - system_command("vmrun -T ws start " + "\"" + @location + "\"") + system_command("vmrun -T ws start " + "\"#{@location}\"") end def stop - system_command("vmrun -T ws stop " + "\"" + @location + "\"") + system_command("vmrun -T ws stop " + "\"#{@location}\"") end def suspend - system_command("vmrun -T ws suspend " + "\"" + @location + "\"") + system_command("vmrun -T ws suspend " + "\"#{@location}\"") end def pause - system_command("vmrun -T ws pause " + "\"" + @location + "\"") + system_command("vmrun -T ws pause " + "\"#{@location}\"") end def reset - system_command("vmrun -T ws reset " + "\"" + @location + "\"") + system_command("vmrun -T ws reset " + "\"#{@location}\"") end def create(snapshot) - system_command("vmrun -T ws snapshot " + "\"" + @location + "\" " + snapshot) + system_command("vmrun -T ws snapshot " + "\"#{@location}\" \"#{@snapshot}\"") end def revert(snapshot) - system_command("vmrun -T ws revertToSnapshot " + "\"" + @location + "\" " + snapshot) + system_command("vmrun -T ws revertToSnapshot " + "\"#{@location}\" \"#{@snapshot}\"") end def delete_snapshot(snapshot) - system_command("vmrun -T ws deleteSnapshot " + "\"" + @location + "\" " + snapshot ) + system_command("vmrun -T ws deleteSnapshot " + "\"#{@location}\" \"#{@snapshot}\"" ) end - def run_command(command, user, pass) - vmrunstr = "vmrun -T ws -gu \"" + user + "\" -gp \"" + pass + "\" runProgramInGuest \"" + - @location + "\" " + "\"" + command + "\" -noWait -activeWindow" + def run_command(command, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + vmrunstr = "vmrun -T ws -gu \"{user}\" -gp \"{pass}\" runProgramInGuest \"#{@location}\" \"{command}\" -noWait -activeWindow" system_command(vmrunstr) end - def copy_from(user, pass, from, to) - vmrunstr = "vmrun -T ws -gu " + user + " -gp " + pass + " copyFileFromGuestToHost \"" + - @location + "\" \"" + from + "\" \"" + to + "\"" + def copy_from(from, to, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + vmrunstr = "vmrun -T ws -gu {user} -gp {pass} copyFileFromGuestToHost \"#{@location}\" \"{from}\" \"{to}\"" system_command(vmrunstr) end - def copy_to(user, pass, from, to) - vmrunstr = "vmrun -T ws -gu " + user + " -gp " + pass + " copyFileFromHostToGuest \"" + - @location + "\" \"" + from + "\" \"" + to + "\"" + def copy_to(from, to, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + + vmrunstr = "vmrun -T ws -gu {user} -gp {pass} copyFileFromHostToGuest \"#{@location}\" \"{from}\" \"{to}\"" system_command(vmrunstr) end - def check_file_exists(user, pass, file) - vmrunstr = "vmrun -T ws -gu " + user + " -gp " + pass + " fileExistsInGuest \"" + - @location + "\" \"" + file + "\" " + def check_file_exists(file, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + + vmrunstr = "vmrun -T ws -gu {user} -gp {pass} fileExistsInGuest \"#{@location}\" \"{file}\" " system_command(vmrunstr) end - def create_directory(user, pass, directory) - vmrunstr = "vmrun -T ws -gu " + user + " -gp " + pass + " createDirectoryInGuest \"" + - @location + "\" \"" + directory + "\" " + def create_directory(directory, named_user=nil) + + ## this will return the first user if named_user doesn't exist + ## -- that may not be entirely obvious... + cred = get_best_credentials(named_user) + + user = cred['user'] + pass = cred['pass'] + admin = cred['admin'] + + vmrunstr = "vmrun -T ws -gu {user} -gp {pass} createDirectoryInGuest \"#{@location}\" \"#{directory}\" " system_command(vmrunstr) end + def cleanup + + end + def running? ## Get running Vms running = `vmrun list` @@ -97,3 +148,6 @@ class WorkstationDriver < VmDriver end end + +end +end