Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Dependencies updated (fog 1.0.0, fission lib vendored for now, ruby-v…

…nc patch included)
  • Loading branch information...
commit 9f1163b53aa3ee82b0776c52bef92b74f6bd2cdb 1 parent 334a8dc
@jedi4ever authored
Showing with 1,772 additions and 67 deletions.
  1. +1 −0  .gitignore
  2. +7 −4 Gemfile
  3. +45 −25 Gemfile.lock
  4. +2 −0  README-LIB.md
  5. +41 −0 lib/fission.rb
  6. +76 −0 lib/fission/cli.rb
  7. +15 −0 lib/fission/command.rb
  8. +68 −0 lib/fission/command/clone.rb
  9. +71 −0 lib/fission/command/delete.rb
  10. +52 −0 lib/fission/command/snapshot_create.rb
  11. +45 −0 lib/fission/command/snapshot_list.rb
  12. +54 −0 lib/fission/command/snapshot_revert.rb
  13. +69 −0 lib/fission/command/start.rb
  14. +31 −0 lib/fission/command/status.rb
  15. +49 −0 lib/fission/command/stop.rb
  16. +67 −0 lib/fission/command/suspend.rb
  17. +29 −0 lib/fission/config.rb
  18. +5 −0 lib/fission/core_ext/class.rb
  19. +7 −0 lib/fission/core_ext/file.rb
  20. +112 −0 lib/fission/core_ext/object.rb
  21. +9 −0 lib/fission/error.rb
  22. +17 −0 lib/fission/fusion.rb
  23. +74 −0 lib/fission/leasesfile.rb
  24. +39 −0 lib/fission/metadata.rb
  25. +16 −0 lib/fission/response.rb
  26. +22 −0 lib/fission/ui.rb
  27. +3 −0  lib/fission/version.rb
  28. +361 −0 lib/fission/vm.rb
  29. +341 −0 lib/net/vnc/vnc.rb
  30. +4 −0 lib/veewee/builder/core/box/vnc.rb
  31. +1 −0  lib/veewee/builder/core/builder/build.rb
  32. +21 −21 lib/veewee/builder/vmfusion/builder.rb
  33. +1 −1  lib/veewee/version.rb
  34. +12 −12 test/build_test.rb
  35. +5 −4 veewee.gemspec
View
1  .gitignore
@@ -13,3 +13,4 @@ pkg/*
*.box
.vagrant
virtualfloppy.vfd
+*.swp
View
11 Gemfile
@@ -2,10 +2,13 @@ source "http://rubygems.org"
gem "veewee", :path => "."
gem "fog", :git => 'git://github.com/geemus/fog.git',:branch => "master"
-gem "fission", "~> 0.4.0a", :git => 'git://github.com/jedi4ever/fission.git',:branch => "enhance_use_as_lib"
-gem "ruby-vnc", :git => 'git://github.com/jedi4ever/ruby-vnc.git',:branch => "master"
-gem "vagrant"
-gem "ruby-libvirt"
+#gem "fog", :git => 'git://github.com/geemus/fog.git',:branch => "master"
+#gem "fog", :git => 'git://github.com/geemus/fog.git',:branch => "master"
+#gem "fission", "~> 0.4.0a", :git => 'git://github.com/jedi4ever/fission.git',:branch => "enhance_use_as_lib"
+#gem "ruby-vnc", :git => 'git://github.com/jedi4ever/ruby-vnc.git',:branch => "master"
+gem "ruby-vnc"
+#gem "vagrant"
+#gem "ruby-libvirt"
#gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git"
View
70 Gemfile.lock
@@ -1,3 +1,34 @@
+GIT
+ remote: git://github.com/geemus/fog.git
+ revision: a90eb43bd26139596a6146c2f242b7997fb87bc5
+ branch: master
+ specs:
+ fog (0.11.0)
+ builder
+ excon (~> 0.7.3)
+ formatador (~> 0.2.0)
+ mime-types
+ multi_json (~> 1.0.3)
+ net-scp (~> 1.0.4)
+ net-ssh (~> 2.1.4)
+ nokogiri (~> 1.5.0)
+ ruby-hmac
+
+GIT
+ remote: git://github.com/jedi4ever/fission.git
+ revision: c7db61e84c77677b7ed591f1c0fe81e52e980fef
+ branch: enhance_use_as_lib
+ specs:
+ fission (0.4.0a)
+ CFPropertyList (~> 2.0.17)
+
+GIT
+ remote: git://github.com/jedi4ever/ruby-vnc.git
+ revision: f4c1ebc3ce02086505d7e92c9bb526a1130f8740
+ branch: master
+ specs:
+ ruby-vnc (1.0.1)
+
PATH
remote: .
specs:
@@ -10,46 +41,35 @@ PATH
popen4 (~> 0.1.2)
progressbar
rspec (~> 2.5.0)
- ruby-vnc (~> 1.0.0)
thor (~> 0.14.6)
-PATH
- remote: /Users/patrick/imac/fog
- specs:
- fog (0.11.0)
- builder
- excon (~> 0.6.5)
- formatador (~> 0.2.0)
- mime-types
- multi_json (~> 1.0.3)
- net-scp (~> 1.0.4)
- net-ssh (~> 2.1.4)
- nokogiri (~> 1.5.0)
- ruby-hmac
-
GEM
remote: http://rubygems.org/
specs:
+ CFPropertyList (2.0.17)
+ libxml-ruby (>= 1.1.0)
+ rake (>= 0.7.0)
Platform (0.4.0)
ansi (1.3.0)
archive-tar-minitar (0.5.2)
builder (3.0.0)
- cucumber (1.0.2)
+ cucumber (1.0.6)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
- gherkin (~> 2.4.5)
+ gherkin (~> 2.4.18)
json (>= 1.4.6)
- term-ansicolor (>= 1.0.5)
- diff-lcs (1.1.2)
+ term-ansicolor (>= 1.0.6)
+ diff-lcs (1.1.3)
erubis (2.7.0)
- excon (0.6.5)
+ excon (0.7.3)
ffi (1.0.9)
- formatador (0.2.0)
- gherkin (2.4.5)
+ formatador (0.2.1)
+ gherkin (2.4.21)
json (>= 1.4.6)
highline (1.6.2)
i18n (0.5.0)
json (1.5.3)
+ libxml-ruby (2.2.2)
mime-types (1.16)
multi_json (1.0.3)
net-scp (1.0.4)
@@ -72,7 +92,6 @@ GEM
rspec-mocks (2.5.0)
ruby-hmac (0.4.0)
ruby-libvirt (0.4.0)
- ruby-vnc (1.0.0)
term-ansicolor (1.0.6)
thor (0.14.6)
vagrant (0.8.2)
@@ -92,9 +111,10 @@ PLATFORMS
DEPENDENCIES
bundler (>= 1.0.0)
+ fission (~> 0.4.0a)!
fog!
rake
- ruby-libvirt (~> 0.4.0)
- ruby-vnc
+ ruby-libvirt
+ ruby-vnc!
vagrant
veewee!
View
2  README-LIB.md
@@ -0,0 +1,2 @@
+this version includes patches for both fission and ruby-vnc
+As the gems are not yet released, they are currently included in the veewee gem
View
41 lib/fission.rb
@@ -0,0 +1,41 @@
+require 'fileutils'
+require 'optparse'
+require 'ostruct'
+require 'yaml'
+
+$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
+
+require 'fission/error'
+require 'fission/cli'
+require 'fission/command'
+require 'fission/command/clone'
+require 'fission/command/snapshot_create'
+require 'fission/command/snapshot_list'
+require 'fission/command/snapshot_revert'
+require 'fission/command/start'
+require 'fission/command/status'
+require 'fission/command/stop'
+require 'fission/command/suspend'
+require 'fission/command/delete'
+require 'fission/config'
+require 'fission/core_ext/class'
+require 'fission/core_ext/file'
+require 'fission/core_ext/object'
+require 'fission/fusion'
+require 'fission/metadata'
+require 'fission/response'
+require 'fission/ui'
+require 'fission/vm'
+require 'fission/version'
+
+module Fission
+ extend self
+
+ def config
+ @config ||= Fission::Config.new
+ end
+
+ def ui
+ @ui ||= Fission::UI.new
+ end
+end
View
76 lib/fission/cli.rb
@@ -0,0 +1,76 @@
+module Fission
+ class CLI
+ def self.execute(args=ARGV)
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\nUsage: fission [options] COMMAND [arguments]"
+
+ opts.on_head('-v', '--version', 'Output the version of fission') do
+ Fission.ui.output Fission::VERSION
+ exit(0)
+ end
+
+ opts.on_head('-h', '--help', 'Displays this message') do
+ show_all_help(optparse)
+ exit(0)
+ end
+
+ opts.define_tail do
+ commands_banner
+ end
+
+ end
+
+ begin
+ optparse.order! args
+ rescue OptionParser::InvalidOption => e
+ Fission.ui.output e
+ show_all_help(optparse)
+ exit(1)
+ end
+
+ if commands.include?(args.first)
+ @cmd = Fission::Command.const_get(args.first.capitalize).new args.drop 1
+ elsif is_snapshot_command?(args)
+ klass = args.take(2).map {|c| c.capitalize}.join('')
+ @cmd = Fission::Command.const_get(klass).new args.drop 2
+ else
+ show_all_help(optparse)
+ exit(1)
+ end
+
+ begin
+ @cmd.execute
+ rescue Error => e
+ puts "Error: #{e}"
+ end
+ end
+
+ def self.commands
+ cmds = Dir.entries(File.join(File.dirname(__FILE__), 'command')).select do |file|
+ !File.directory? file
+ end
+
+ cmds.map { |cmd| File.basename(cmd, '.rb').gsub '_', ' ' }
+ end
+
+ private
+ def self.is_snapshot_command?(args)
+ args.first == 'snapshot' && args.count > 1 && commands.include?(args.take(2).join(' '))
+ end
+
+ def self.commands_banner
+ text = "\nCommands:\n"
+ Fission::Command.descendants.each do |command_klass|
+ text << (command_klass.send :help)
+ end
+
+ text
+ end
+
+ def self.show_all_help(options)
+ Fission.ui.output options
+ Fission.ui.output commands_banner
+ end
+
+ end
+end
View
15 lib/fission/command.rb
@@ -0,0 +1,15 @@
+module Fission
+ class Command
+ attr_reader :options, :args
+
+ def initialize(args=[])
+ @options = OpenStruct.new
+ @args = args
+ end
+
+ def self.help
+ self.new.option_parser.to_s
+ end
+
+ end
+end
View
68 lib/fission/command/clone.rb
@@ -0,0 +1,68 @@
+module Fission
+ class Command
+ class Clone < Command
+
+ def initialize(args=[])
+ super
+ @options.start = false
+ end
+
+ def execute
+ option_parser.parse! @args
+
+ unless @args.count > 1
+ Fission.ui.output self.class.help
+ Fission.ui.output ""
+ Fission.ui.output_and_exit "Incorrect arguments for clone command", 1
+ end
+
+ source_vm_name = @args.first
+ target_vm_name = @args[1]
+ source_vm=Fission::VM.new(source_vm_name)
+ target_vm=Fission::VM.new(target_vm_name)
+
+ unless source_vm.exists?
+ Fission.ui.output_and_exit "Unable to find the source vm #{source_vm_name} (#{source_vm.path})", 1
+ end
+
+ if target_vm.exists?
+ Fission::ui.output_and_exit "The target vm #{target_vm_name} already exists", 1
+ end
+
+ clone_task = Fission::VM.clone source_vm_name, target_vm_name
+
+ if clone_task.successful?
+ Fission.ui.output ''
+ Fission.ui.output 'Clone complete!'
+
+ if @options.start
+ Fission.ui.output "Starting '#{target_vm_name}'"
+
+ start_task = target_vm.start
+
+ if start_task.successful?
+ Fission.ui.output "VM '#{target_vm_name}' started"
+ else
+ Fission.ui.output_and_exit "There was an error starting the VM. The error was:\n#{start_task.output}", start_task.code
+ end
+ end
+ else
+ Fission.ui.output_and_exit "There was an error cloning the VM. The error was:\n#{clone_task.output}", clone_task.code
+ end
+ end
+
+ def option_parser
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\nclone usage: fission clone source_vm target_vm [options]"
+
+ opts.on '--start', 'Start the VM after cloning' do
+ @options.start = true
+ end
+ end
+
+ optparse
+ end
+
+ end
+ end
+end
View
71 lib/fission/command/delete.rb
@@ -0,0 +1,71 @@
+module Fission
+ class Command
+ class Delete < Command
+
+ def initialize(args=[])
+ super
+ @options.force = false
+ end
+
+ def execute
+ option_parser.parse! @args
+
+ if @args.count < 1
+ Fission.ui.output self.class.help
+ Fission.ui.output ""
+ Fission.ui.output_and_exit "Incorrect arguments for delete command", 1
+ end
+
+ target_vm_name = @args.first
+ target_vm=Fission::VM.new(target_vm_name)
+ unless target_vm.exists?
+ Fission.ui.output_and_exit "Vm #{target_vm_name} does not exist at (#{target_vm.path})", 1
+ end
+
+ if target_vm.running?
+ Fission.ui.output 'VM is currently running'
+ if @options.force
+ Fission.ui.output 'Going to stop it'
+ Fission::Command::Stop.new([target_vm_name]).execute
+ else
+ Fission.ui.output_and_exit "Either stop/suspend the VM or use '--force' and try again.", 1
+ end
+ end
+
+
+ if Fission::Fusion.running?
+ Fission.ui.output 'It looks like the Fusion GUI is currently running'
+
+ if @options.force
+ Fission.ui.output 'The Fusion metadata for the VM may not be removed completely'
+ else
+ Fission.ui.output "Either exit the Fusion GUI or use '--force' and try again"
+ Fission.ui.output_and_exit "NOTE: Forcing a VM deletion with the Fusion GUI running may not clean up all of the VM metadata", 1
+ end
+ end
+
+ delete_task = Fission::VM.delete target_vm_name
+
+ if delete_task.successful?
+ Fission.ui.output ''
+ Fission.ui.output "Deletion complete!"
+ else
+ Fission.ui.output_and_exit "There was an error deleting the VM. The error was:\n#{delete_task.output}", delete_task.code
+ end
+ end
+
+ def option_parser
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\ndelete usage: fission delete target_vm [--force]"
+
+ opts.on '--force', "Stop the VM if it's running and then delete it" do
+ @options.force = true
+ end
+ end
+
+ optparse
+ end
+
+ end
+ end
+end
View
52 lib/fission/command/snapshot_create.rb
@@ -0,0 +1,52 @@
+module Fission
+ class Command
+ class SnapshotCreate < Command
+
+ def initialize(args=[])
+ super
+ end
+
+ def execute
+ unless @args.count == 2
+ Fission.ui.output self.class.help
+ Fission.ui.output ""
+ Fission.ui.output_and_exit "Incorrect arguments for snapshot create command", 1
+ end
+
+ vm_name, snap_name = @args.take 2
+
+ vm = Fission::VM.new vm_name
+ unless vm.exists?
+ Fission.ui.output_and_exit "Unable to find the VM #{vm_name} (#{Fission::VM.path(vm_name)})", 1
+ end
+
+ unless vm.running?
+ Fission.ui.output "VM '#{vm_name}' is not running"
+ Fission.ui.output_and_exit 'A snapshot cannot be created unless the VM is running', 1
+ end
+
+ if vm.snapshots.include? snap_name
+ Fission.ui.output_and_exit "VM '#{vm_name}' already has a snapshot named '#{snap_name}'", 1
+ end
+
+ Fission.ui.output "Creating snapshot"
+ task = vm.create_snapshot(snap_name)
+
+ if task.successful?
+ Fission.ui.output "Snapshot '#{snap_name}' created"
+ else
+ Fission.ui.output_and_exit "There was an error creating the snapshot. The error was:\n#{task.output}", task.code
+ end
+ end
+
+ def option_parser
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\nsnapshot create: fission snapshot create my_vm snapshot_1"
+ end
+
+ optparse
+ end
+
+ end
+ end
+end
View
45 lib/fission/command/snapshot_list.rb
@@ -0,0 +1,45 @@
+module Fission
+ class Command
+ class SnapshotList < Command
+
+ def initialize(args=[])
+ super
+ end
+
+ def execute
+ unless @args.count == 1
+ Fission.ui.output self.class.help
+ Fission.ui.output ""
+ Fission.ui.output_and_exit "Incorrect arguments for snapshot list command", 1
+ end
+
+ vm_name = @args.first
+
+ vm = Fission::VM.new vm_name
+
+ unless vm.exists?
+ Fission.ui.output_and_exit "Unable to find the VM #{vm_name} (#{vm.path})", 1
+ end
+
+ snaps=vm.snapshots
+ unless snaps.empty?
+ Fission.ui.output snaps.join("\n")
+ else
+ Fission.ui.output "No snapshots found for VM '#{vm_name}'"
+ end
+
+ # TODO
+ Fission.ui.output_and_exit "There was an error listing the snapshots. The error was:\n#{task.output}", task.code
+ end
+
+ def option_parser
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\nsnapshot list: fission snapshot list my_vm"
+ end
+
+ optparse
+ end
+
+ end
+ end
+end
View
54 lib/fission/command/snapshot_revert.rb
@@ -0,0 +1,54 @@
+module Fission
+ class Command
+ class SnapshotRevert < Command
+
+ def initialize(args=[])
+ super
+ end
+
+ def execute
+ unless @args.count == 2
+ Fission.ui.output self.class.help
+ Fission.ui.output ''
+ Fission.ui.output_and_exit 'Incorrect arguments for snapshot revert command', 1
+ end
+
+ vm_name, snap_name = @args.take 2
+ vm = Fission::VM.new vm_name
+
+ unless vm.exists? vm_name
+ Fission.ui.output_and_exit "Unable to find the VM #{vm_name} (#{Fission::VM.path(vm_name)})", 1
+ end
+
+ if Fission::Fusion.running?
+ Fission.ui.output 'It looks like the Fusion GUI is currently running'
+ Fission.ui.output_and_exit 'Please exit the Fusion GUI and try again', 1
+ end
+
+ snaps = vm.snapshots
+
+ unless snaps.include? snap_name
+ Fission.ui.output_and_exit "Unable to find the snapshot '#{snap_name}'", 1
+ end
+
+ Fission.ui.output "Reverting to snapshot '#{snap_name}'"
+ task = vm.revert_to_snapshot snap_name
+
+ if task.successful?
+ Fission.ui.output "Reverted to snapshot '#{snap_name}'"
+ else
+ Fission.ui.output_and_exit "There was an error reverting to the snapshot. The error was:\n#{task.output}", task.code
+ end
+ end
+
+ def option_parser
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\nsnapshot revert: fission snapshot revert my_vm snapshot_1"
+ end
+
+ optparse
+ end
+
+ end
+ end
+end
View
69 lib/fission/command/start.rb
@@ -0,0 +1,69 @@
+module Fission
+ class Command
+ class Start < Command
+
+ def initialize(args=[])
+ super
+ @options.headless = false
+ end
+
+ def execute
+ option_parser.parse! @args
+
+ if @args.empty?
+ Fission.ui.output self.class.help
+ Fission.ui.output ""
+ Fission.ui.output_and_exit "Incorrect arguments for start command", 1
+ end
+
+ vm_name = @args.first
+
+ vm = Fission::VM.new(vm_name)
+
+ unless vm.exists?
+ Fission.ui.output_and_exit "Unable to find the VM #{vm_name} (#{Fission::VM.path(vm_name)})", 1
+ end
+
+ if vm.running?
+ Fission.ui.output ''
+ Fission.ui.output_and_exit "VM '#{vm_name}' is already running", 0
+ end
+
+ Fission.ui.output "Starting '#{vm_name}'"
+ start_args = {}
+
+ if @options.headless
+
+ if Fission::Fusion.running?
+ Fission.ui.output 'It looks like the Fusion GUI is currently running'
+ Fission.ui.output 'A VM cannot be started in headless mode when the Fusion GUI is running'
+ Fission.ui.output_and_exit "Exit the Fusion GUI and try again", 1
+ else
+ start_args[:headless] = true
+ end
+ end
+
+ task = vm.start(start_args)
+
+ if task.successful?
+ Fission.ui.output "VM '#{vm_name}' started"
+ else
+ Fission.ui.output_and_exit "There was a problem starting the VM. The error was:\n#{task.output}", task.code
+ end
+ end
+
+ def option_parser
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\nstart usage: fission start vm [options]"
+
+ opts.on '--headless', 'Start the VM in headless mode (i.e. no Fusion GUI console)' do
+ @options.headless = true
+ end
+ end
+
+ optparse
+ end
+
+ end
+ end
+end
View
31 lib/fission/command/status.rb
@@ -0,0 +1,31 @@
+module Fission
+ class Command
+ class Status < Command
+
+ def initialize(args=[])
+ super
+ end
+
+ def execute
+
+ all_vms=Fission::VM.all
+ vm_with_longest_name = all_vms.max { |a,b| a.name.length <=> b.name.length }
+ max_name_length=vm_with_longest_name.name.length
+ all_vms.each do |vm|
+ status = vm.state
+ Fission.ui.output_printf "%-#{max_name_length}s %s\n", vm.name, "["+status+"]"
+ end
+
+ end
+
+ def option_parser
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\nstatus usage: fission status"
+ end
+
+ optparse
+ end
+
+ end
+ end
+end
View
49 lib/fission/command/stop.rb
@@ -0,0 +1,49 @@
+module Fission
+ class Command
+ class Stop < Command
+
+ def initialize(args=[])
+ super
+ end
+
+ def execute
+ unless @args.count == 1
+ Fission.ui.output self.class.help
+ Fission.ui.output ""
+ Fission.ui.output_and_exit "Incorrect arguments for stop command", 1
+ end
+
+ vm_name = @args.first
+ vm = Fission::VM.new vm_name
+
+ unless vm.exists?
+ Fission.ui.output_and_exit "VM #{vm_name} does not exist at (#{vm.path})", 1
+ end
+
+
+ unless vm.running?
+ Fission.ui.output ''
+ Fission.ui.output_and_exit "VM '#{vm_name}' is not running", 0
+ end
+
+ Fission.ui.output "Stopping '#{vm_name}'"
+ task = vm.stop
+
+ if task.successful?
+ Fission.ui.output "VM '#{vm_name}' stopped"
+ else
+ Fission.ui.output_and_exit "There was an error stopping the VM. The error was:\n#{response.output}", response.code
+ end
+ end
+
+ def option_parser
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\nstop usage: fission stop vm"
+ end
+
+ optparse
+ end
+
+ end
+ end
+end
View
67 lib/fission/command/suspend.rb
@@ -0,0 +1,67 @@
+module Fission
+ class Command
+ class Suspend < Command
+
+ def initialize(args=[])
+ super
+ @options.all = false
+ end
+
+ def execute
+ option_parser.parse! @args
+
+ if @args.count != 1 && !@options.all
+ Fission.ui.output self.class.help
+ Fission.ui.output ""
+ Fission.ui.output_and_exit "Incorrect arguments for suspend command", 1
+ end
+
+ vms_to_suspend.each do |vm|
+ Fission.ui.output "Suspending '#{vm.name}'"
+ task = vm.suspend
+
+ if task.successful?
+ Fission.ui.output "VM '#{vm.name}' suspended"
+ else
+ Fission.ui.output_and_exit "There was an error suspending the VM. The error was:\n#{task.output}", task.code
+ end
+ end
+ end
+
+ def vms_to_suspend
+ if @options.all
+ vms=Fission::VM.all_running
+ else
+ vm_name = @args.first
+ vm=Fission::VM.new(vm_name)
+
+ unless vm.exists?
+ Fission.ui.output ''
+ Fission.ui.output_and_exit "VM #{vm_name} does not exist (#{Fission::VM.path(vm_name)})", 1
+ end
+
+ unless vm.running?
+ Fission.ui.output ''
+ Fission.ui.output_and_exit "VM '#{vm_name}' is not running", 1
+ end
+
+ vms = [vm]
+ end
+ vms
+ end
+
+ def option_parser
+ optparse = OptionParser.new do |opts|
+ opts.banner = "\nsuspend usage: fission suspend [vm | --all]"
+
+ opts.on '--all', 'Suspend all running VMs' do
+ @options.all = true
+ end
+ end
+
+ optparse
+ end
+
+ end
+ end
+end
View
29 lib/fission/config.rb
@@ -0,0 +1,29 @@
+module Fission
+ class Config
+ attr_accessor :attributes
+
+ CONF_FILE = File.expand_path '~/.fissionrc'
+
+ def initialize
+ @attributes = {}
+ load_from_file
+
+ if @attributes['vm_dir'].blank?
+ @attributes['vm_dir'] = File.expand_path('~/Documents/Virtual Machines.localized/')
+ end
+
+ @attributes['vmrun_bin'] = '/Library/Application Support/VMware Fusion/vmrun'
+ @attributes['vmrun_cmd'] = "#{@attributes['vmrun_bin'].gsub(' ', '\ ')} -T fusion"
+ @attributes['plist_file'] = File.expand_path('~/Library/Preferences/com.vmware.fusion.plist')
+ @attributes['gui_bin'] = File.expand_path('/Applications/VMware Fusion.app/Contents/MacOS/vmware')
+ end
+
+ private
+ def load_from_file
+ if File.file?(CONF_FILE)
+ @attributes.merge!(YAML.load_file(CONF_FILE))
+ end
+ end
+
+ end
+end
View
5 lib/fission/core_ext/class.rb
@@ -0,0 +1,5 @@
+class Class
+ def descendants
+ ObjectSpace.each_object(Class).select { |klass| klass < self }
+ end
+end
View
7 lib/fission/core_ext/file.rb
@@ -0,0 +1,7 @@
+class File
+ # from ptools
+ def self.binary?(file)
+ s = (File.read(file, File.stat(file).blksize) || "").split(//)
+ ((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
+ end
+end
View
112 lib/fission/core_ext/object.rb
@@ -0,0 +1,112 @@
+# this is from active_support
+# github.com/rails/rails/activesupport
+#
+class Object
+ # An object is blank if it's false, empty, or a whitespace string.
+ # For example, "", " ", +nil+, [], and {} are blank.
+ #
+ # This simplifies:
+ #
+ # if !address.nil? && !address.empty?
+ #
+ # ...to:
+ #
+ # if !address.blank?
+ def blank?
+ respond_to?(:empty?) ? empty? : !self
+ end
+
+ # An object is present if it's not <tt>blank?</tt>.
+ def present?
+ !blank?
+ end
+
+ # Returns object if it's <tt>present?</tt> otherwise returns +nil+.
+ # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
+ #
+ # This is handy for any representation of objects where blank is the same
+ # as not present at all. For example, this simplifies a common check for
+ # HTTP POST/query parameters:
+ #
+ # state = params[:state] if params[:state].present?
+ # country = params[:country] if params[:country].present?
+ # region = state || country || 'US'
+ #
+ # ...becomes:
+ #
+ # region = params[:state].presence || params[:country].presence || 'US'
+ def presence
+ self if present?
+ end
+end
+
+class NilClass
+ # +nil+ is blank:
+ #
+ # nil.blank? # => true
+ #
+ def blank?
+ true
+ end
+end
+
+class FalseClass
+ # +false+ is blank:
+ #
+ # false.blank? # => true
+ #
+ def blank?
+ true
+ end
+end
+
+class TrueClass
+ # +true+ is not blank:
+ #
+ # true.blank? # => false
+ #
+ def blank?
+ false
+ end
+end
+
+class Array
+ # An array is blank if it's empty:
+ #
+ # [].blank? # => true
+ # [1,2,3].blank? # => false
+ #
+ alias_method :blank?, :empty?
+end
+
+class Hash
+ # A hash is blank if it's empty:
+ #
+ # {}.blank? # => true
+ # {:key => 'value'}.blank? # => false
+ #
+ alias_method :blank?, :empty?
+end
+
+class String
+ # A string is blank if it's empty or contains whitespaces only:
+ #
+ # "".blank? # => true
+ # " ".blank? # => true
+ # " something here ".blank? # => false
+ #
+ def blank?
+ self !~ /\S/
+ end
+end
+
+class Numeric #:nodoc:
+ # No number is blank:
+ #
+ # 1.blank? # => false
+ # 0.blank? # => false
+ #
+ def blank?
+ false
+ end
+end
View
9 lib/fission/error.rb
@@ -0,0 +1,9 @@
+module Fission
+ class Error < StandardError
+ attr_reader :orginal
+ def initialize(msg, original=$!)
+ super(msg)
+ @original = original; end
+ end
+end
+
View
17 lib/fission/fusion.rb
@@ -0,0 +1,17 @@
+module Fission
+ class Fusion
+
+ def self.running?
+ command = "ps -ef | grep -v grep | grep -c "
+ command << "#{Fission.config.attributes['gui_bin'].gsub(' ', '\ ')} 2>&1"
+ output = `#{command}`
+
+ response = Fission::Response.new :code => 0
+
+ output.strip.to_i > 0 ? response.data = true : response.data = false
+
+ response
+ end
+
+ end
+end
View
74 lib/fission/leasesfile.rb
@@ -0,0 +1,74 @@
+require 'date'
+
+class Lease
+ attr_accessor :name,:mac,:start,:end,:ip
+
+ def initialize(name)
+ @name=name
+ @ip=name
+ end
+
+ def expired?
+ @end < DateTime.now
+ end
+end
+
+class LeasesFile
+
+ attr_reader :leases
+
+ def initialize(filename)
+ @filename=filename
+ @leases=Array.new
+ load
+ end
+
+ def load
+ @leases=Array.new
+ File.open(@filename,"r") do |leasefile|
+ lease=nil
+ while (line = leasefile.gets)
+
+ line=line.lstrip.gsub(';','')
+ case line
+ when /^lease/
+ @leases << lease unless lease.nil?
+ name=line.split(' ')[1]
+ lease=Lease.new(name)
+ when /^hardware/
+ lease.mac=line.split(" ")[2]
+ when /^starts/
+ lease.start=DateTime.parse(line.split(" ")[2..3].join(" "))
+ when /^ends/
+ lease.end=DateTime.parse(line.split(" ")[2..3].join(" "))
+ end
+
+ end
+ @leases << lease unless lease.nil?
+ end
+ return @leases
+ end
+
+ def all_leases
+ return @leases
+ end
+
+ def current_leases
+ hash_list=Hash.new
+ @leases.each do |lease|
+ hash_list[lease.name]=lease
+ end
+ collapsed_list=Array.new
+ hash_list.each do |key,value|
+ collapsed_list << value
+ end
+ return collapsed_list
+ end
+
+ def find_lease_by_mac(mac)
+ matches=current_leases.select{|l| l.mac==mac}
+ return nil if matches.nil?
+ return matches[0]
+ end
+
+end
View
39 lib/fission/metadata.rb
@@ -0,0 +1,39 @@
+module Fission
+ class Metadata
+
+ require 'cfpropertylist'
+
+ attr_accessor :content
+
+ def self.delete_vm_info(vm_path)
+ metadata = new
+ metadata.load
+ metadata.delete_vm_restart_document(vm_path)
+ metadata.delete_vm_favorite_entry(vm_path)
+ metadata.save
+ end
+
+ def load
+ raw_data = CFPropertyList::List.new :file => Fission.config.attributes['plist_file']
+ @content = CFPropertyList.native_types raw_data.value
+ end
+
+ def save
+ new_content = CFPropertyList::List.new
+ new_content.value = CFPropertyList.guess @content
+ new_content.save Fission.config.attributes['plist_file'],
+ CFPropertyList::List::FORMAT_BINARY
+ end
+
+ def delete_vm_restart_document(vm_path)
+ if @content.has_key?('PLRestartDocumentPaths')
+ @content['PLRestartDocumentPaths'].delete_if { |p| p == vm_path }
+ end
+ end
+
+ def delete_vm_favorite_entry(vm_path)
+ @content['VMFavoritesListDefaults2'].delete_if { |vm| vm['path'] == vm_path }
+ end
+
+ end
+end
View
16 lib/fission/response.rb
@@ -0,0 +1,16 @@
+module Fission
+ class Response
+ attr_accessor :code, :output, :data
+
+ def initialize(args={})
+ @code = args.fetch :code, 1
+ @output = args.fetch :output, ''
+ @data = args.fetch :data, nil
+ end
+
+ def successful?
+ @code == 0
+ end
+
+ end
+end
View
22 lib/fission/ui.rb
@@ -0,0 +1,22 @@
+module Fission
+ class UI
+ attr_reader :stdout
+
+ def initialize(stdout=$stdout)
+ @stdout = stdout
+ end
+
+ def output(s)
+ @stdout.puts s
+ end
+
+ def output_printf(string, key, value)
+ @stdout.send :printf, string, key, value
+ end
+
+ def output_and_exit(s, exit_code)
+ output s
+ exit exit_code
+ end
+ end
+end
View
3  lib/fission/version.rb
@@ -0,0 +1,3 @@
+module Fission
+ VERSION = "0.4.0a"
+end
View
361 lib/fission/vm.rb
@@ -0,0 +1,361 @@
+require 'fission/leasesfile'
+require 'shellwords'
+require 'fission/error'
+
+module Fission
+ class VM
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ #####################################################
+ # Path Helpers
+ #####################################################
+ # Returns the topdir of the vm
+ def path
+ File.join Fission.config.attributes['vm_dir'], "#{@name}.vmwarevm"
+ end
+
+ # Returns a string to the path of the config file
+ # There is no guarantee it exists
+ def vmx_path
+ return File.join(path, "#{@name}.vmx")
+ end
+
+
+ ####################################################################
+ # State information
+ ####################################################################
+ def running?
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+
+ command = "#{vmrun_cmd} list"
+ output = `#{command}`
+
+ response = Fission::Response.new :code => $?.exitstatus
+
+ if response.successful?
+ vms = output.split("\n").select do |vm|
+ vm.include?('.vmx') && File.exists?(vm) && File.extname(vm) == '.vmx'
+ end
+ return vms.include?(self.vmx_path)
+ else
+ raise Fission::Error,"Error listing the state of vm #{@name}:\n#{output}"
+ end
+ end
+
+ def suspended?
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+
+ suspend_filename=File.join(File.dirname(vmx_path), File.basename(vmx_path,".vmx")+".vmem")
+ return File.exists?(suspend_filename)
+ end
+
+ # Checks to see if a vm exists
+ def exists?
+ File.exists? vmx_path
+ end
+
+ # Returns the state of a vm
+ def state
+ return "not created" unless self.exists?
+
+ return "suspend" if self.suspended?
+
+ return "running" if self.running?
+
+ return "not running"
+ end
+
+ ####################################################################
+ # VM information
+ ####################################################################
+
+ # Returns an Array of snapshot names
+ def snapshots
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+
+ command = "#{vmrun_cmd} listSnapshots #{vmx_path.shellescape} 2>&1"
+ output = `#{command}`
+
+ raise "There was an error listing the snapshots of #{@name} :\n #{output}" unless $?.exitstatus==0
+
+ snaps_unfiltered = output.split("\n").select { |s| !s.include? 'Total snapshots:' }
+ snaps=snaps_unfiltered.map { |s| s.strip }
+ return snaps
+ end
+
+ # Retrieve the first mac address for a vm
+ # This will only retrieve the first auto generate mac address
+ def mac_address
+ raise ::Fission::Error,"VM #{@name} does not exist" unless self.exists?
+
+ line=File.new(vmx_path).grep(/^ethernet0.generatedAddress =/)
+ if line.nil?
+ #Fission.ui.output "Hmm, the vmx file #{vmx_path} does not contain a generated mac address "
+ return nil
+ end
+ address=line.first.split("=")[1].strip.split(/\"/)[1]
+ return address
+ end
+
+ # Retrieve the ip address for a vm.
+ # This will only look for dynamically assigned ip address via vmware dhcp
+ def ip_address
+ raise ::Fission::Error,"VM #{@name} does not exist" unless self.exists?
+
+ unless mac_address.nil?
+ lease=LeasesFile.new("/var/db/vmware/vmnet-dhcpd-vmnet8.leases").find_lease_by_mac(mac_address)
+ if lease.nil?
+ return nil
+ else
+ return lease.ip
+ end
+ else
+ # No mac address was found for this machine so we can't calculate the ip-address
+ return nil
+ end
+ end
+
+ ####################################################################
+ # VMS information
+ ####################################################################
+
+ # Returns an array of vm objects
+ def self.all
+ vm_dirs = Dir[File.join Fission.config.attributes['vm_dir'], '*.vmwarevm'].select do |d|
+ File.directory? d
+ end
+
+ vm_names=vm_dirs.map { |d| File.basename d, '.vmwarevm' }
+ vms=[]
+ vm_names.each do |vmname|
+ vm=Fission::VM.new vmname
+ vms << vm
+ end
+
+ return vms
+ end
+
+ # Returns an array of vms that are running
+ def self.all_running
+ running_vms=self.all.select do |vm|
+ vm.state=="running"
+ end
+ return running_vms
+ end
+
+ # Returns an existing vm
+ def self.get(name)
+ return Fission::VM.new(name)
+ end
+
+ #####################################################
+ # VM Class Actions
+ #####################################################
+ def self.clone(source_vm, target_vm)
+ raise Fission::Error,"VM #{source_vm} does not exist" unless Fission::VM.new(source_vm).exists?
+ raise Fission::Error,"VM #{target_vm} already exists" if Fission::VM.new(target_vm).exists?
+
+ FileUtils.cp_r Fission::VM.new(source_vm).path, Fission::VM.new(target_vm).path
+
+ rename_vm_files source_vm, target_vm
+ update_config source_vm, target_vm
+
+ response = Response.new :code => 0
+ end
+
+ def self.delete(vm_name)
+ raise Fission::Error,"VM #{vm_name} does not exist" unless Fission::VM.new(vm_name).exists?
+
+ vm=Fission::VM.new(vm_name)
+ FileUtils.rm_rf vm.path
+ Fission::Metadata.delete_vm_info(vm.path)
+
+ Response.new :code => 0
+ end
+
+
+ #####################################################
+ # VM Instance Actions
+ #####################################################
+ def create_snapshot(name)
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+
+ command = "#{vmrun_cmd} snapshot #{vmx_path.shellescape} \"#{name}\" 2>&1"
+ output = `#{command}`
+
+ response = Fission::Response.new :code => $?.exitstatus
+ response.output = output unless response.successful?
+
+ response
+ end
+
+ def start(args={})
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+ raise Fission::Error,"VM #{@name} is already started" if self.running?
+
+
+ command = "#{vmrun_cmd} start #{vmx_path.shellescape}"
+
+ if !args[:headless].blank? && args[:headless]
+ command << " nogui 2>&1"
+ else
+ command << " gui 2>&1"
+ end
+
+ output = `#{command}`
+
+ response = Fission::Response.new :code => $?.exitstatus
+ response.output = output unless response.successful?
+
+ response
+ end
+
+ def stop
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+ raise Fission::Error,"VM #{@name} is not running" unless self.running?
+
+ command = "#{vmrun_cmd} stop #{vmx_path.shellescape} 2>&1"
+ output = `#{command}`
+
+ response = Fission::Response.new :code => $?.exitstatus
+ response.output = output unless response.successful?
+
+ response
+ end
+
+ def halt
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+ raise Fission::Error,"VM #{@name} is not running" unless self.running?
+
+ command = "#{vmrun_cmd} stop #{vmx_path.shellescape} hard 2>&1"
+ output = `#{command}`
+
+ response = Fission::Response.new :code => $?.exitstatus
+ response.output = output unless response.successful?
+
+ response
+ end
+
+ def resume
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+ raise Fission::Error,"VM #{@name} is already running" if self.running?
+ if self.suspended?
+ self.start
+ end
+ end
+
+ def suspend
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+ raise Fission::Error,"VM #{@name} is not running" unless self.running?
+
+ command = "#{vmrun_cmd} suspend #{vmx_path.shellescape} hard 2>&1"
+ output = `#{command}`
+
+ response = Fission::Response.new :code => $?.exitstatus
+ response.output = output unless response.successful?
+ response
+ end
+
+ # Action to revert to a snapshot
+ # Returns a response object
+ def revert_to_snapshot(name)
+ raise Fission::Error,"VM #{@name} does not exist" unless self.exists?
+
+ command = "#{vmrun_cmd} revertToSnapshot #{vmx_path.shellescape} \"#{name}\" 2>&1"
+ output = `#{command}`
+
+ response = Fission::Response.new :code => $?.exitstatus
+ response.output = output unless response.successful?
+
+ response
+ end
+
+ #####################################################
+ # Helpers
+ #####################################################
+ private
+ def self.rename_vm_files(from, to)
+ to_vm=Fission::VM.new(to)
+
+ files_to_rename(from, to).each do |filename|
+ text_to_replace = File.basename(filename, File.extname(filename))
+
+ if File.extname(filename) == '.vmdk'
+ if filename.match /\-s\d+\.vmdk/
+ text_to_replace = filename.partition(/\-s\d+.vmdk/).first
+ end
+ end
+
+ unless File.exists?(File.join(to_vm.path, filename.gsub(text_to_replace, to)))
+ FileUtils.mv File.join(to_vm.path, filename),
+ File.join(to_vm.path, filename.gsub(text_to_replace, to))
+ end
+ end
+ end
+
+ def self.files_to_rename(from, to)
+ files_which_match_source_vm = []
+ other_files = []
+
+ from_vm=Fission::VM.new(from)
+
+ Dir.entries(from_vm.path).each do |f|
+ unless f == '.' || f == '..'
+ f.include?(from) ? files_which_match_source_vm << f : other_files << f
+ end
+ end
+
+ files_which_match_source_vm + other_files
+ end
+
+ def self.vm_file_extensions
+ ['.nvram', '.vmdk', '.vmem', '.vmsd', '.vmss', '.vmx', '.vmxf']
+ end
+
+ # This is done after a clone has been done
+ # All files are already at the to location
+ # The content of the text files will be substituted with strings from => to
+ def self.update_config(from, to)
+ to_vm=Fission::VM.new(to)
+
+ ['.vmx', '.vmxf', '.vmdk'].each do |ext|
+ file = File.join to_vm.path, "#{to}#{ext}"
+
+ unless File.binary?(file)
+ text = (File.read file).gsub from, to
+ File.open(file, 'w'){ |f| f.print text }
+ end
+
+ end
+
+ # Rewrite vmx file to avoid messages
+ new_vmx_file=File.open(File.join(to_vm.vmx_path),'r')
+
+ content=new_vmx_file.read
+
+ # Filter out other values
+ content=content.gsub(/^tools.remindInstall.*\n/, "")
+ content=content.gsub(/^uuid.action.*\n/,"").strip
+
+ # Remove generate mac addresses
+ content=content.gsub(/^ethernet.+generatedAddress.*\n/,"").strip
+
+ # Add the correct values
+ content=content+"\ntools.remindInstall = \"FALSE\"\n"
+ content=content+"uuid.action = \"create\"\n"
+
+ # Now rewrite the vmx file
+ File.open(new_vmx_file,'w'){ |f| f.print content}
+
+ end
+
+ def vmrun_cmd
+ return Fission.config.attributes['vmrun_cmd']
+ end
+
+ end
+end
View
341 lib/net/vnc/vnc.rb
@@ -0,0 +1,341 @@
+require 'socket'
+require 'yaml'
+require 'thread'
+require 'cipher/des'
+require 'net/vnc/version'
+
+module Net
+ #
+ # The VNC class provides for simple rfb-protocol based control of
+ # a VNC server. This can be used, eg, to automate applications.
+ #
+ # Sample usage:
+ #
+ # # launch xclock on localhost. note that there is an xterm in the top-left
+ # Net::VNC.open 'localhost:0', :shared => true, :password => 'mypass' do |vnc|
+ # vnc.pointer_move 10, 10
+ # vnc.type 'xclock'
+ # vnc.key_press :return
+ # end
+ #
+ # = TODO
+ #
+ # * The server read loop seems a bit iffy. Not sure how best to do it.
+ # * Should probably be changed to be more of a lower-level protocol wrapping thing, with the
+ # actual VNCClient sitting on top of that. all it should do is read/write the packets over
+ # the socket.
+ #
+ class VNC
+ class PointerState
+ attr_reader :x, :y, :button
+
+ def initialize vnc
+ @x = @y = @button = 0
+ @vnc = vnc
+ end
+
+ # could have the same for x=, and y=
+ def button= button
+ @button = button
+ refresh
+ end
+
+ def update x, y, button=@button
+ @x, @y, @button = x, y, button
+ refresh
+ end
+
+ def refresh
+ packet = 0.chr * 6
+ packet[0] = 5.chr
+ packet[1] = button.chr
+ packet[2, 2] = [x].pack 'n'
+ packet[4, 2] = [y].pack 'n'
+ @vnc.socket.write packet
+ end
+ end
+
+ BASE_PORT = 5900 if BASE_PORT.nil?
+ CHALLENGE_SIZE = 16 if CHALLENGE_SIZE.nil?
+ DEFAULT_OPTIONS = {
+ :shared => false,
+ :wait => 0.1
+ } if DEFAULT_OPTIONS.nil?
+
+ if KEY_MAP.nil?
+ keys_file = File.dirname(__FILE__) + '/../../data/keys.yaml'
+ KEY_MAP = YAML.load_file(keys_file).inject({}) { |h, (k, v)| h.update k.to_sym => v }
+ def KEY_MAP.[] key
+ super or raise ArgumentError.new('Invalid key name - %s' % key)
+ end
+ end
+
+ attr_reader :server, :display, :options, :socket, :pointer
+
+ def initialize display=':0', options={}
+ @server = 'localhost'
+ if display =~ /^(.*)(:\d+)$/
+ @server, display = $1, $2
+ end
+ @display = display[1..-1].to_i
+ @options = DEFAULT_OPTIONS.merge options
+ @clipboard = nil
+ @pointer = PointerState.new self
+ @mutex = Mutex.new
+ connect
+ @packet_reading_state = nil
+ @packet_reading_thread = Thread.new { packet_reading_thread }
+ end
+
+ def self.open display=':0', options={}
+ vnc = new display, options
+ if block_given?
+ begin
+ yield vnc
+ ensure
+ vnc.close
+ end
+ else
+ vnc
+ end
+ end
+
+ def port
+ BASE_PORT + @display
+ end
+
+ def connect
+ @socket = TCPSocket.open server, port
+ unless socket.read(12) =~ /^RFB (\d{3}.\d{3})\n$/
+ raise 'invalid server response'
+ end
+ @server_version = $1
+ socket.write "RFB 003.003\n"
+ data = socket.read(4)
+ auth = data.to_s.unpack('N')[0]
+ case auth
+ when 0, nil
+ raise 'connection failed'
+ when 1
+ # ok...
+ when 2
+ password = @options[:password] or raise 'Need to authenticate but no password given'
+ challenge = socket.read CHALLENGE_SIZE
+ response = Cipher::DES.encrypt password, challenge
+ socket.write response
+ ok = socket.read(4).to_s.unpack('N')[0]
+ raise 'Unable to authenticate - %p' % ok unless ok == 0
+ else
+ raise 'Unknown authentication scheme - %d' % auth
+ end
+
+ # ClientInitialisation
+ socket.write((options[:shared] ? 1 : 0).chr)
+
+ # ServerInitialisation
+ # TODO: parse this.
+ socket.read(20)
+ data = socket.read(4)
+ # read this many bytes in chunks of 20
+ size = data.to_s.unpack('N')[0]
+ while size > 0
+ len = [20, size].min
+ # this is the hostname, and other stuff i think...
+ socket.read(len)
+ size -= len
+ end
+ end
+
+ # this types +text+ on the server
+ def type text, options={}
+ packet = 0.chr * 8
+ packet[0] = 4.chr
+ text.split(//).each do |char|
+ packet[7] = char[0]
+ packet[1] = 1.chr
+ socket.write packet
+ packet[1] = 0.chr
+ socket.write packet
+ end
+ wait options
+ end
+
+ SHIFTED_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+{}|:"<>?'
+ KEY_PRESS_CHARS = {
+ "\n" => :return,
+ "\t" => :tab
+ }
+
+ # This types +text+ on the server, but it holds the shift key down when necessary.
+ # It will also execute key_press for tabs and returns.
+ def type_string text, options={}
+ shift_key_down = nil
+
+ text.each_char do |char|
+ key_to_press = KEY_PRESS_CHARS[char]
+ unless key_to_press.nil?
+ key_press key_to_press
+ else
+ key_needs_shift = SHIFTED_CHARS.include? char
+
+ if shift_key_down.nil? || shift_key_down != key_needs_shift
+ if key_needs_shift
+ key_down :shift
+ else
+ key_up :shift
+ end
+ end
+
+ type char
+ shift_key_down = key_needs_shift
+ end
+ end
+ wait options
+ end
+
+ # this takes an array of keys, and successively holds each down then lifts them up in
+ # reverse order.
+ # FIXME: should wait. can't recurse in that case.
+ def key_press(*args)
+ options = Hash === args.last ? args.pop : {}
+ keys = args
+ raise ArgumentError, 'Must have at least one key argument' if keys.empty?
+ begin
+ key_down keys.first
+ if keys.length == 1
+ yield if block_given?
+ else
+ key_press(*(keys[1..-1] + [options]))
+ end
+ ensure
+ key_up keys.first
+ end
+ end
+
+ def get_key_code which
+ if String === which
+ if which.length != 1
+ raise ArgumentError, 'can only get key_code of single character strings'
+ end
+ which[0]
+ else
+ KEY_MAP[which]
+ end
+ end
+ private :get_key_code
+
+ def key_down which, options={}
+ packet = 0.chr * 8
+ packet[0] = 4.chr
+ key_code = get_key_code which
+ packet[4, 4] = [key_code].pack('N')
+ packet[1] = 1.chr
+ socket.write packet
+ wait options
+ end
+
+ def key_up which, options={}
+ packet = 0.chr * 8
+ packet[0] = 4.chr
+ key_code = get_key_code which
+ packet[4, 4] = [key_code].pack('N')
+ packet[1] = 0.chr
+ socket.write packet
+ wait options
+ end
+
+ def pointer_move x, y, options={}
+ # options[:relative]
+ pointer.update x, y
+ wait options
+ end
+
+ BUTTON_MAP = {
+ :left => 0
+ } if BUTTON_MAP.nil?
+
+ def button_press button=:left, options={}
+ begin
+ button_down button, options
+ yield if block_given?
+ ensure
+ button_up button, options
+ end
+ end
+
+ def button_down which=:left, options={}
+ button = BUTTON_MAP[which] || which
+ raise ArgumentError, 'Invalid button - %p' % which unless (0..2) === button
+ pointer.button |= 1 << button
+ wait options
+ end
+
+ def button_up which=:left, options={}
+ button = BUTTON_MAP[which] || which
+ raise ArgumentError, 'Invalid button - %p' % which unless (0..2) === button
+ pointer.button &= ~(1 << button)
+ wait options
+ end
+
+ def wait options={}
+ sleep options[:wait] || @options[:wait]
+ end
+
+ def close
+ # destroy packet reading thread
+ if @packet_reading_state == :loop
+ @packet_reading_state = :stop
+ while @packet_reading_state
+ # do nothing
+ end
+ end
+ socket.close
+ end
+
+ def clipboard
+ if block_given?
+ @clipboard = nil
+ yield
+ 60.times do
+ clipboard = @mutex.synchronize { @clipboard }
+ return clipboard if clipboard
+ sleep 0.5
+ end
+ warn 'clipboard still empty after 30s'
+ nil
+ else
+ @mutex.synchronize { @clipboard }
+ end
+ end
+
+ private
+
+ def read_packet type
+ case type
+ when 3 # ServerCutText
+ socket.read 3 # discard padding bytes
+ len = socket.read(4).unpack('N')[0]
+ @mutex.synchronize { @clipboard = socket.read len }
+ else
+ raise NotImplementedError, 'unhandled server packet type - %d' % type
+ end
+ end
+
+ def packet_reading_thread
+ @packet_reading_state = :loop
+ loop do
+ begin
+ break if @packet_reading_state != :loop
+ next unless IO.select [socket], nil, nil, 2
+ type = socket.read(1)[0]
+ read_packet type
+ rescue
+ warn "exception in packet_reading_thread: #{$!.class}:#{$!}"
+ break
+ end
+ end
+ @packet_reading_state = nil
+ end
+ end
+end
+
View
4 lib/veewee/builder/core/box/vnc.rb
@@ -1,5 +1,9 @@
+# Include the gem library
require 'net/vnc'
+# Monkey patch the vnc
+require 'net/vnc/vnc.rb'
+
module Veewee
module Builder
module Core
View
1  lib/veewee/builder/core/builder/build.rb
@@ -95,6 +95,7 @@ def build(definition_name,box_name,options)
env.ui.info "You can now login to the box with:"
env.ui.info "\nssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p #{ssh_options(definition)[:port]} -l #{definition.ssh_user} #{box.ip_address}"
+ return box
end
def filter_postinstall_files(definition,options)
View
42 lib/veewee/builder/vmfusion/builder.rb
@@ -11,9 +11,9 @@ class Builder < Veewee::Builder::Core::Builder
def check_requirements
- unless gem_available?("fission")
- raise ::Veewee::Error, "The Vmfusion Builder requires the gem 'fission' to be installed\n"+ "gem install fission"
- end
+ #unless gem_available?("fission")
+ #raise ::Veewee::Error, "The Vmfusion Builder requires the gem 'fission' to be installed\n"+ "gem install fission"
+ #end
end
def build_info
@@ -37,37 +37,37 @@ def ssh_options(definition)
#
-
+
# Transfer information provide by the builder to the box
#
#
- def transfer_buildinfo(box,definition)
- super(box,definition)
-
- # When we get here, ssh is available and no postinstall scripts have been executed yet
- # So we begin by transferring the ISO file of the vmware tools
-
- iso_image="/Library/Application Support/VMware Fusion/isoimages/linux.iso"
- iso_image="/Library/Application Support/VMware Fusion/isoimages/darwin.iso" if definition.os_type_id=~/^Darwin/
+ def transfer_buildinfo(box,definition)
+ super(box,definition)
+
+ # When we get here, ssh is available and no postinstall scripts have been executed yet
+ # So we begin by transferring the ISO file of the vmware tools
+
+ iso_image="/Library/Application Support/VMware Fusion/isoimages/linux.iso"
+ iso_image="/Library/Application Support/VMware Fusion/isoimages/darwin.iso" if definition.os_type_id=~/^Darwin/
iso_image="/Library/Application Support/VMware Fusion/isoimages/freebsd.iso" if definition.os_type_id=~/^Free/
iso_image="/Library/Application Support/VMware Fusion/isoimages/windows.iso" if definition.os_type_id=~/^Win/
-
+
begin
when_ssh_login_works(box.ip_address,ssh_options(definition).merge({:timeout => definition.postinstall_timeout.to_i})) do
- begin
- env.logger.info "About to transfer vmware tools iso buildinfo to the box #{box.name} - #{box.ip_address} - #{ssh_options(definition)}"
- ssh_transfer_file(box.ip_address,iso_image,File.basename(iso_image),ssh_options(definition))
- rescue RuntimeError => ex
- env.ui.error "Error transfering vmware tools iso , possible not enough permissions to write? #{ex}"
- exit -1
- end
+ begin
+ env.logger.info "About to transfer vmware tools iso buildinfo to the box #{box.name} - #{box.ip_address} - #{ssh_options(definition)}"
+ ssh_transfer_file(box.ip_address,iso_image,File.basename(iso_image),ssh_options(definition))
+ rescue RuntimeError => ex
+ env.ui.error "Error transfering vmware tools iso , possible not enough permissions to write? #{ex}"
+ exit -1
+ end
end
rescue Net::SSH::AuthenticationFailed
env.ui.error "Authentication failure"
exit -1
end
- end
+ end
View
2  lib/veewee/version.rb
@@ -4,5 +4,5 @@ module Veewee
# Only set the version constant if it wasn't set before
unless defined?(Veewee::VERSION)
- ::Veewee::VERSION="0.3.0"
+ ::Veewee::VERSION="0.3.0a"
end
View
24 test/build_test.rb
@@ -22,19 +22,19 @@ def test_virtualbox_1_build
end
# def test_virtualbox_2_ssh
-# assert_nothing_raised {
-# result=@ve.config.builders["virtualbox"].get_box(@box_name).ssh("who am i")
-# assert_match(/root/,result.stdout)
-# }
-# end
+ #assert_nothing_raised {
+ #result=@ve.config.builders["virtualbox"].get_box(@box_name).ssh("who am i")
+ #assert_match(/root/,result.stdout)
+ #}
+ #end
#
-# def test_virtualbox_3_console_type
-# assert_nothing_raised {
-# @ve.builder(:virtualbox).get_box(@vm_name,@vd,{}).console_type('echo "bla" > console.txt<Enter>')
-# result=@ve.builder(:virtualbox).get_box(@vm_name,@vd,{}).ssh("cat console.txt")
-# assert_match(/bla/,result.stdout)
-# }
-# end
+ def test_virtualbox_3_console_type
+ assert_nothing_raised {
+ @ve.config.builders["virtualbox"].get_box(@box_name).console_type('echo "bla" > console.txt<Enter>')
+ result=@ve.builder["virtualbox"].get_box(@box_name).ssh("cat console.txt")
+ assert_match(/bla/,result.stdout)
+ }
+ end
#
# def test_virtualbox_4_destroy
# assert_nothing_raised {
View
9 veewee.gemspec
@@ -14,8 +14,7 @@ Gem::Specification.new do |s|
s.required_rubygems_version = ">= 1.3.6"
s.rubyforge_project = "veewee"
-# s.add_dependency "vagrant", "~> 0.8.2"
-# s.add_dependency "libvirt"
+ s.add_dependency "vagrant", "~> 0.8.2"
s.add_dependency "net-ssh", "~> 2.1.0"
s.add_dependency "popen4", "~> 0.1.2"
s.add_dependency "thor", "~> 0.14.6"
@@ -26,10 +25,12 @@ Gem::Specification.new do |s|
s.add_dependency "rspec", "~> 2.5.0"
s.add_dependency "ansi", "~> 1.3.0"
s.add_dependency "ruby-vnc", "~> 1.0.0"
- #s.add_dependency "simon", "~> 0.1.1"
+ s.add_dependency "fog", "~> 1.0.0"
+ s.add_dependency "CFPropertyList", "~> 2.0.17"
+# s.add_dependency "libvirt"
s.add_development_dependency "bundler", ">= 1.0.0"
- s.add_development_dependency('ruby-libvirt','~>0.4.0')
+ #s.add_development_dependency('ruby-libvirt','~>0.4.0')
s.files = `git ls-files`.split("\n")
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
Please sign in to comment.
Something went wrong with that request. Please try again.