virtuoso-ensure-state 0.0.6.beta

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,23 @@
1
+ 0.0.6.beta
2
+ ====
3
+ * Fix permission issues in gem deployment
4
+
5
+ 0.0.5.beta
6
+ ====
7
+ * Sync code versioning schema
8
+
9
+ 0.0.4.beta
10
+ ====
11
+ * Up version to fix back library push
12
+
13
+ 0.0.3.beta
14
+ ====
15
+ * Fix issue with require paths in gemspec
16
+
17
+ 0.0.2.beta
18
+ =====
19
+ * Initial feature complete release of virtualbox backend
20
+
21
+ 0.0.1.beta
22
+ =====
23
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright (C) 2010 3Crowd Technologies, Inc.
2
+
3
+ This program is free software: you can redistribute it and/or modify
4
+ it under the terms of the GNU General Public License as published by
5
+ the Free Software Foundation, either version 3 of the License, or
6
+ (at your option) any later version.
7
+
8
+ This program is distributed in the hope that it will be useful,
9
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ GNU General Public License for more details.
12
+
13
+ You should have received a copy of the GNU General Public License
14
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
data/README ADDED
@@ -0,0 +1,12 @@
1
+ README for Virtuoso-ensure-state
2
+ =============================
3
+ Virtuoso-ensure-state is a program which converges virtual machines into a certain state. Essentially,
4
+ it's an easy way to specify a state machine to ensure that your virtual machine remain in a state you
5
+ control.
6
+
7
+ Example
8
+ =======
9
+ ./virtuoso-ensure-state -b virtualbox -m myvirtualmachine --ifpowered_off start --ifpaused resume --no-gui --silent
10
+
11
+ would silently start the virtualbox based virtual machine if it were powered off and would resume it if it were paused, and would
12
+ attempt to start it in the background in a non-gui mode.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Ensure library path is in load path
4
+ library_path = File.join(File.dirname(__FILE__),'..','lib')
5
+ $:.push(library_path) unless $:.include?(library_path)
6
+
7
+ require 'ensure-state'
8
+ require 'ensure-state/cli'
9
+
10
+ Virtuoso::EnsureState::CLI.run!(ARGV)
@@ -0,0 +1,3 @@
1
+ #Ensure the ensure-state library directory is on the include path
2
+ include_path_location = File.join(File.dirname(__FILE__),'ensure-state')
3
+ $:.push(include_path_location) unless $:.include?(include_path_location)
@@ -0,0 +1,84 @@
1
+ require 'ensure-state/cli/option_parser'
2
+ require 'ensure-state/engine'
3
+
4
+ require 'logger'
5
+
6
+ module Virtuoso
7
+ module EnsureState
8
+ class CLI
9
+
10
+ class << self
11
+
12
+ # Creates and starts a new CLI instance for ensure-state.
13
+ # By default reads options from command line ARGV.
14
+ def run!(run_flags=ARGV)
15
+ self.new(run_flags).run!
16
+ end
17
+
18
+ end
19
+
20
+ attr_reader :logger
21
+
22
+ def initialize(raw_run_flags)
23
+ @raw_run_flags = raw_run_flags
24
+ @logger = Logger.new(STDOUT)
25
+
26
+ # defaults
27
+
28
+ @logger.level = Logger::WARN
29
+ @logger.progname = self.class.name
30
+ end
31
+
32
+ # Starts a virtual machine convergence run using the ensure-state system guided by the run_flags passed into the initializer.
33
+ def run!
34
+ cli_parser = parse_run_flags(@raw_run_flags)
35
+ # display any informational messages emitted by the parser
36
+ puts cli_parser.messages.join("\n") unless cli_parser.messages.empty?
37
+ # exit if suggested by parser
38
+ exit(cli_parser.suggested_exit_return_code) if cli_parser.exit_after_message_display_suggested
39
+
40
+ # setup logger
41
+ logger.level = Logger::DEBUG if cli_parser.options[:debug]
42
+ logger.level = Logger::INFO if cli_parser.options[:verbose]
43
+
44
+ # nothing within this application should log at a level higher than fatal
45
+ logger.level = Logger::UNKNOWN if cli_parser.options[:silent]
46
+
47
+ logger.debug "Completed parsing command line arguments. Starting convergence engine."
48
+
49
+ display_mode = cli_parser.options[:nogui] ? :vrdp : :gui
50
+
51
+ # this is where all the actual work happens
52
+ engine = EnsureState::Engine.new(cli_parser.options[:backend], cli_parser.options[:machine], display_mode, cli_parser.options[:ifinstate], @logger)
53
+
54
+ begin
55
+ engine.converge_vm_state!
56
+ rescue Errors::BackendUnavailableError => e
57
+ logger.fatal "Unable to access backend. Message: (#{e.message})"
58
+ exit(1)
59
+ rescue Errors::StateTransitionError => e
60
+ logger.fatal "Could not transition to state. Message: (#{e.message})"
61
+ exit(1)
62
+ rescue Errors::VMNotFoundError => e
63
+ logger.fatal "Unable to find VM. Message: (#{e.message})"
64
+ exit(1)
65
+ rescue Errors::InvalidVMBackendError => e
66
+ logger.fatal "Unable to find specified VM Backend. Message: (#{e.message})"
67
+ exit(1)
68
+ rescue StandardError => e
69
+ logger.fatal "Fatal Error: #{e.inspect}"
70
+ logger.fatal "Debug Backtrace: \n#{e.backtrace.join("\n")}" if cli_parser.options[:debug]
71
+ exit(1)
72
+ end
73
+
74
+ end
75
+
76
+ private
77
+
78
+ def parse_run_flags(run_flags_to_parse)
79
+ CLIOptionParser.new(run_flags_to_parse)
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,145 @@
1
+ require 'optparse' #stdlib
2
+ require 'ensure-state/version'
3
+
4
+ module Virtuoso
5
+ module EnsureState
6
+ class CLIOptionParser
7
+
8
+ STATES = [:powered_off, :running, :saved, :paused]
9
+ ACTIONS = [:start, :pause, :stop, :shutdown, :resume]
10
+ BACKENDS = [:virtualbox]
11
+
12
+ attr_reader :options
13
+ attr_reader :messages
14
+ attr_reader :exit_after_message_display_suggested
15
+ attr_reader :suggested_exit_return_code
16
+
17
+ def initialize(args)
18
+ @messages = Array.new
19
+ @options = Hash.new
20
+ @exit_after_message_display_suggested = false
21
+ @suggested_exit_return_code = 0
22
+ parse!(args)
23
+ end
24
+
25
+ private
26
+
27
+ def parse!(args)
28
+
29
+ # If we have no options, we need to display usage, which is the help option
30
+ args << '-h' if args.empty?
31
+
32
+ # The options specified on the command line will be collected in *options*
33
+ # We set defaults here.
34
+ options[:machine] = nil
35
+ options[:ifinstate] = Hash.new
36
+
37
+ opts = OptionParser.new do |opts|
38
+ opts.banner = "Usage: virtuoso-ensure-state [required options] [options]"
39
+ opts.separator "Example: virtuoso-ensure-state -b virtualbox -m development_vm --ifPoweredOff powerUp"
40
+ opts.separator "Version: #{Version.to_s}"
41
+
42
+ opts.separator ""
43
+ opts.separator "Required options:"
44
+
45
+ # Mandatory arguments
46
+ opts.on('-b', '--backend BACKEND',
47
+ "Backend to use") do |backend|
48
+ options[:backend] = backend.to_sym
49
+ end
50
+
51
+ opts.on('-m', '--machine MACHINE',
52
+ 'Virtual Machine name or UUID this application should manage') do |machine|
53
+ options[:machine] = machine
54
+ end
55
+
56
+ opts.separator ""
57
+ opts.separator "available BACKENDs: #{BACKENDS.join(', ')}"
58
+
59
+ opts.separator ""
60
+ opts.separator "Specific options:"
61
+
62
+ # Optional arguments
63
+ STATES.each do |state|
64
+
65
+ opts.on("--if#{state} ACTION",
66
+ "Action to take if a given virtual machine is in state #{state}") do |action|
67
+ options[:ifinstate][state] = action.to_sym
68
+ end
69
+
70
+ end
71
+
72
+ opts.separator ""
73
+ opts.separator "Available ACTIONs: #{ACTIONS.join(', ')}"
74
+ opts.separator ""
75
+ opts.separator "Common options:"
76
+
77
+ opts.on('--no-gui', "Attempt to run virtual machines in no-gui mode (dependent on backend)") do
78
+ options[:nogui] = true
79
+ end
80
+
81
+ opts.on('-s', "--silent", "Run silently (no output)") do
82
+ options[:silent] = true
83
+ end
84
+
85
+ opts.on('-v', "--[no-]verbose", "Run verbosely") do |v|
86
+ options[:verbose] = v
87
+ end
88
+
89
+ opts.on('-d', "--debug", "Run with debugging messages and behavior turned on") do
90
+ options[:debug] = true
91
+ end
92
+
93
+ # No argument, shows at tail. This will print an options summary.
94
+ opts.on_tail('-h', '--help', 'Show this message') do
95
+ @messages << opts.to_s
96
+ options[:help_requested] = true
97
+ suggest_exit_after_message_display
98
+ end
99
+
100
+ opts.on_tail('-v', '--version', 'Show application version') do
101
+ @messages << Version.to_s
102
+ suggest_exit_after_message_display
103
+ end
104
+
105
+ end
106
+
107
+ opts.parse!(args)
108
+
109
+ ensure_required_switches_passed!
110
+
111
+ end
112
+
113
+ def ensure_required_switches_passed!
114
+ return if @options[:help_requested]
115
+
116
+ if @options[:backend].nil? || @options[:machine].nil? then
117
+ @messages << 'Error: Required option not set. Please ensure all required options have been set. See usage by using -h flag.'
118
+ suggest_exit_after_message_display
119
+ suggest_exit_return_code(1)
120
+ end
121
+
122
+ unless @options[:backend] && BACKENDS.member?(@options[:backend])
123
+ @messages << 'Error: Invalid backend specified. Please ensure backend exists in available backend list. See available backends using -h flag.'
124
+ suggest_exit_after_message_display
125
+ suggest_exit_return_code(1)
126
+ end
127
+
128
+ unless @options[:ifinstate] && @options[:ifinstate].values.all?{|value|ACTIONS.member?(value)}
129
+ @messages << 'Error: Invalid action specified for :ifinstate. Ensure all actions are set appropriately. See usage and available actions by using the -h flag.'
130
+ suggest_exit_after_message_display
131
+ suggest_exit_return_code(1)
132
+ end
133
+ end
134
+
135
+ def suggest_exit_return_code(code)
136
+ @suggested_exit_return_code = code
137
+ end
138
+
139
+ def suggest_exit_after_message_display
140
+ @exit_after_message_display_suggested = true
141
+ end
142
+
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,81 @@
1
+ require 'ensure-state/vm_backend/virtualbox'
2
+
3
+ require 'ensure-state/vm'
4
+
5
+ require 'ensure-state/errors/invalid_vm_backend_error'
6
+
7
+ module Virtuoso
8
+ module EnsureState
9
+
10
+ class Engine
11
+
12
+ attr_reader :state_and_action_map, :logger
13
+
14
+ # Instantiate a new engine for converging vm state
15
+ #
16
+ # @param [EnsureState::VMBackend::Base] backend The VM Backend where the machine specified in vm_name can be found
17
+ # @param [String] vm_name A name or UUID which uniquely identifies the virtual machine inside the backend specified in backend
18
+ # @param [Hash<Symbol, Symbol>] state_and_action_map A mapping of states, if a VM is in one of those keyed states, take the specified action. Otherwise, do nothing
19
+ # @param [Logger] logger The logger for engine information
20
+ def initialize backend, vm_name, vm_display_mode, state_and_action_map, logger = Logger.new(STDOUT)
21
+ @backend_name = backend
22
+ @vm_display_mode = vm_display_mode
23
+ @vm_name = vm_name
24
+ @state_and_action_map = state_and_action_map
25
+ @logger = logger.dup
26
+ @logger.progname = self.class.name
27
+ end
28
+
29
+ # Converges the specified backend VM state utilizing the state_and_action_map
30
+ #
31
+ # @raise [Errors::BackendUnavailableError] The specified backend was unavailable for interaction
32
+ # @raise [Errors::StateTransitionError] There was an error transitioning a VirtualMachine into a specified state
33
+ # @raise [Errors::VMNotFoundError] The VM specified as not found by the backend
34
+ # @raise [Errors::InvalidVMBackendError] The specified VM Backend was not registered
35
+ def converge_vm_state!
36
+ logger.debug "Converging VM State"
37
+ action_to_take = state_and_action_map[vm.state]
38
+ if action_to_take.nil?
39
+ logger.info "No action specified for current virtual machine state (#{vm.state})"
40
+ else
41
+ logger.info "Taking action: #{action_to_take}"
42
+ vm.state = action_to_take if backend.can_transition_to_state?(vm.state, action_to_take)
43
+ end
44
+ end
45
+
46
+ # The backend in which the specified virtual machine should be found
47
+ #
48
+ # @raise [Errors::InvalidVMBackendError] The specified VM Backend was not registered
49
+ def backend
50
+ @backend ||= load_backend(@backend_name)
51
+ end
52
+
53
+ # The virtual machine to be operated on by this engine
54
+ #
55
+ # @raise [Errors::VMNotFoundError] The VM specified was not found by the backend
56
+ def vm
57
+ logger.debug("Finding VM (#{@vm_name})")
58
+ return @vm if @vm
59
+ @vm = VM.find!(@vm_name, backend, logger)
60
+ @vm.display_mode = @vm_display_mode
61
+ @vm
62
+ end
63
+
64
+ private
65
+
66
+ def load_backend(backend)
67
+ logger.debug "Finding backend for load call: #{backend}"
68
+ backend_instance = case backend
69
+ when :virtualbox
70
+ VMBackend::VirtualBox.new(logger)
71
+ else
72
+ raise Errors::InvalidVMBackendError, "Specified backend (#{backend}) is not a registered backend"
73
+ end
74
+ logger.debug "Found backend for load call: #{backend}"
75
+ return backend_instance
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,10 @@
1
+ module Virtuoso
2
+ module EnsureState
3
+ module Errors
4
+
5
+ class EnsureStateError < StandardError
6
+ end
7
+
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ require 'ensure-state/errors/ensure_state_error'
2
+
3
+ module Virtuoso
4
+ module EnsureState
5
+ module Errors
6
+
7
+ class InvalidVMBackendError < EnsureStateError
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'ensure-state/errors/ensure_state_error'
2
+
3
+ module Virtuoso
4
+ module EnsureState
5
+ module Errors
6
+
7
+ class VMBackendError < EnsureStateError
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'ensure-state/errors/vm_backend_error'
2
+
3
+ module Virtuoso
4
+ module EnsureState
5
+ module Errors
6
+
7
+ class BackendUnavailableError < VMBackendError
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'ensure-state/errors/vm_backend_error'
2
+
3
+ module Virtuoso
4
+ module EnsureState
5
+ module Errors
6
+
7
+ class InvalidStateError < VMBackendError
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'ensure-state/errors/vm_backend_error'
2
+
3
+ module Virtuoso
4
+ module EnsureState
5
+ module Errors
6
+
7
+ class StateTransitionError < VMBackendError
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'ensure-state/errors/vm_backend_error'
2
+
3
+ module Virtuoso
4
+ module EnsureState
5
+ module Errors
6
+
7
+ class VMNotFoundError < VMBackendError
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Virtuoso
2
+ module EnsureState
3
+ class Version
4
+
5
+ MAJOR = '0'
6
+ MINOR = '0'
7
+ POINT = '6'
8
+ RELEASE_MARKER = 'beta'
9
+
10
+ def self.to_s
11
+ [MAJOR,MINOR,POINT,RELEASE_MARKER].join('.')
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,101 @@
1
+ require 'ensure-state/vm_backend/base'
2
+ require 'ensure-state/errors/vm_backend_errors/vm_not_found_error'
3
+
4
+ module Virtuoso
5
+ module EnsureState
6
+ class VM
7
+
8
+ # Find a virtual machine instance on the specified backend
9
+ #
10
+ # @params [String] name_or_uuid The name or uuid of the vm to find
11
+ # @params [VMBackend::Base] backend a virtual machine backend implementation,
12
+ # usage of the abstract implementation is not recommended
13
+ # @return [VM, false] The virtual machine instance, or false if the instance is
14
+ # is not found
15
+ def self.find name_or_uuid, backend, logger = Logger.new(STDOUT)
16
+ begin
17
+ self.find! name_or_uuid, backend, logger
18
+ rescue Errors::VMNotFoundError => e
19
+ false
20
+ end
21
+ end
22
+
23
+ # Find a virtual machine instance on the specified backend, raising an exception
24
+ # if the virtual machine is not found
25
+ #
26
+ # @params [String] name_or_uuid The name or uuid of the vm to find
27
+ # @params [VMBackend::Base] backend a virtual machine backend implementation,
28
+ # usage of the abstract implementation is not recommended
29
+ # @return [VM, false] The virtual machine instance
30
+ # @raise [VMNotFoundError] Exception raised if virtual machine is not found
31
+ def self.find! name_or_uuid, backend, logger = Logger.new(STDOUT)
32
+ self.new name_or_uuid, backend, logger
33
+ end
34
+
35
+ attr_accessor :display_mode
36
+ attr_reader :vm_name, :backend, :logger
37
+
38
+ def initialize name_or_uuid, backend, logger = Logger.new(STDOUT)
39
+ @logger = logger.dup
40
+ @logger.progname = self.class.name
41
+
42
+ @display_mode = nil
43
+ @vm_name = name_or_uuid
44
+ @backend = backend
45
+ verify_backend_is_a_valid_backend @backend
46
+ @vm = retrieve_backend_reference_to_named_vm @backend, @vm_name
47
+ end
48
+
49
+ # The human readable name of the VM, may be the UUID if there is no human readable
50
+ # name assigned by the backend
51
+ #
52
+ # @return [String] The human readable Virtual Machine name, or uuid if no human readable
53
+ # name is available
54
+ def name
55
+ name = @backend.vm_name(@vm)
56
+ unless name
57
+ uuid
58
+ end
59
+ end
60
+
61
+ # The state of the vm.
62
+ #
63
+ # @return [Symbol] The state of the virtual machine
64
+ def state
65
+ state = @backend.vm_state(@vm)
66
+ end
67
+
68
+ # Set the state of the vm
69
+ #
70
+ # @raise [Errors::InvalidStateError] Error raised if the specified state is not recognized by the backend, or if the current
71
+ # state of the virtual machine does not allow for setting the specified state
72
+ def state= state
73
+ @backend.vm_set_state!(@vm, state, { :mode => display_mode })
74
+ end
75
+
76
+ # The unique identifier for the VM. Note: The score of the uniqueness is guaranteed by
77
+ # the backend. It may not be universally unique
78
+ #
79
+ # @return [String] The Virtual Machine unique identifier
80
+ def uuid
81
+ @backend.vm_uuid(@vm)
82
+ end
83
+
84
+ private
85
+
86
+ def verify_backend_is_a_valid_backend backend
87
+ logger.debug("Verifying backend type")
88
+ backend.kind_of? VMBackend::Base
89
+ end
90
+
91
+ def retrieve_backend_reference_to_named_vm backend, named_vm
92
+ logger.debug("Retrieving backend reference to named vm. Backend: #{backend}. Named VM: #{named_vm}")
93
+ vm = backend.vm_find named_vm
94
+ raise Errors::VMNotFoundError if vm.nil?
95
+ logger.debug("Successfully retrieved backend reference to named vm. Backend Reference Class: #{vm.class.name}")
96
+ vm
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,96 @@
1
+ require 'ensure-state/errors/vm_backend_errors/state_transition_error'
2
+
3
+ module Virtuoso
4
+ module EnsureState
5
+ module VMBackend
6
+
7
+ class Base
8
+
9
+ class << self
10
+
11
+ # The logger for EnsureState::VMBackend objects
12
+ #
13
+ # @return [Logger] The logger
14
+ def logger
15
+ @@logger
16
+ end
17
+
18
+ # Set the logger used for EnsureState::VMBackend objects
19
+ #
20
+ # @param [Logger] logger A logger conforming to the Ruby 1.8+ Logger class interface
21
+ def logger= logger
22
+ @@logger = logger
23
+ end
24
+
25
+ end
26
+
27
+ VALID_ACTION_TO_STATE_APPLICATION_MAP = {
28
+ }
29
+
30
+ # Find a virtual machine on the backend
31
+ #
32
+ # @param [String] name_or_uuid the name or uuid to find
33
+ # @return [EnsureState::VM, false] the virtual machine or false if the machine was not found
34
+ def vm_find name_or_uuid
35
+ raise NotImplementedError, "#find is not implemented on the abstract VM Backend, please use a concrete implementation"
36
+ end
37
+
38
+ # Retrieve the human readable name of the given virtual machine or false if no human readable name
39
+ # is available
40
+ #
41
+ # @param [EnsureState::VM] virtual_machine The virtual machine on which the name should be retrieved
42
+ # @return [String, false] The human readable name of the virtual machine, or false if no human readable name is available
43
+ def vm_name virtual_machine
44
+ raise NotImplementedError, "#vm_name is not implemented on the abstract VM Backend, please use a concrete implementation"
45
+ end
46
+
47
+ # Retrieve the unique identifier for given virtual machine. This should always be available. The unique
48
+ # identifier (or uuid) is an arbitrary token, chosen by the backend, that uniquely identifies the virtual machine
49
+ # within a scope to be decided by the backend
50
+ #
51
+ # @param [EnsureState::VM] virtual_machine The virtual machine whose uuid should be retrieved
52
+ # @return [String] The unique identifier
53
+ def vm_uuid virtual_machine
54
+ raise NotImplementedError, "#vm_uuid is not implemented on the abstract VM Backend, please use a concrete implementation"
55
+ end
56
+
57
+ # Retrieve the state of a given virtual machine
58
+ #
59
+ # @param [EnsureState::VM] virtual_machine The virtual machine whose state should be retrieved
60
+ # @return [Symbol] The state of the virtual machine
61
+ def vm_state virtual_machine
62
+ raise NotImplementedError, "#vm_state is not implemented on the abstract VM Backend, please use a concrete implementation"
63
+ end
64
+
65
+ # Attempt to set the state of a given virtual machine. You must check the return value to ensure the state of of the virtual
66
+ # machine in the backend. To be certain please use vm_set_state! which raises exceptions if the specified state could not
67
+ # be reached
68
+ #
69
+ # @param [Ensure_State::VM] virtual_machine The virtual machine whose state should be set
70
+ # @param [Symbol] state The state to which the machine should be moved
71
+ # @return [Boolean] True if machine has successfully been moved to the specified state, False if not
72
+ def vm_set_state virtual_machine, state
73
+ begin
74
+ vm_set_state! virtual_machine, state
75
+ rescue Errors::StateTransitionError => e
76
+ return false
77
+ end
78
+ true
79
+ end
80
+
81
+ # Set the state of a given virtual machine. If the state cannot be reached from the current state or a failure occurs in setting
82
+ # an exception is raised, depending on the specific error. The exception thrown will always subclass Errors::StateTransitionError
83
+ #
84
+ # @param [Ensure_State::VM] virtual_machine The virtual machine whose state will be set
85
+ # @param [Symbol] state The state to which the machine will be moved
86
+ # @raise [Errors::StateTransitionError] An exception, or a subclass thereof, which is raised when the specified state could not be reached.
87
+ # The reason for which is specified by the particular subclass of StateTransitionError
88
+ def vm_set_state! virtual_machine, state
89
+ riase NotImplementedError, "#vm_set_state! is not implemented on the abstract VM Backend, please use a concrete implementation"
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,118 @@
1
+ require 'ensure-state/errors/vm_backend_errors/backend_unavailable_error'
2
+ require 'ensure-state/errors/vm_backend_errors/invalid_state_error'
3
+ require 'ensure-state/errors/vm_backend_errors/vm_not_found_error'
4
+
5
+ require 'ensure-state/vm_backend/base'
6
+
7
+ require 'rubygems'
8
+ require 'virtualbox'
9
+
10
+ module Virtuoso
11
+ module EnsureState
12
+ module VMBackend
13
+
14
+ class VirtualBox < Base
15
+
16
+ VALID_ACTION_TO_STATE_APPLICATION_MAP = {
17
+ :start => [:powered_off, :saved],
18
+ :pause => [:running],
19
+ :saved_state => [:running],
20
+ :stop => [:running],
21
+ :shutdown => [:running],
22
+ :resume => [:paused]
23
+ }
24
+
25
+ VM_BACKEND_SETTLE_TIME = 0.5 #seconds
26
+
27
+ attr_reader :logger
28
+
29
+ def initialize(logger = Logger.new(STDOUT))
30
+ @logger = logger.dup
31
+ @logger.progname = self.class.name
32
+
33
+ logger.debug("Initializing VirtualBox Backend")
34
+
35
+ @backend = ::VirtualBox
36
+ ensure_valid_backend_version @backend
37
+ end
38
+
39
+ def vm_find name_or_uuid
40
+ logger.debug("Finding VM (#{name_or_uuid})")
41
+ vm = @backend::VM.find(name_or_uuid)
42
+ logger.debug("Found VM (#{name_or_uuid})") if vm
43
+ vm
44
+ end
45
+
46
+ def vm_name virtual_machine
47
+ virtual_machine.name
48
+ end
49
+
50
+ def vm_uuid virtual_machine
51
+ virtual_machine.uuid
52
+ end
53
+
54
+ def vm_state virtual_machine
55
+ virtual_machine.state
56
+ end
57
+
58
+ def vm_set_state! virtual_machine, state_to_set, options = {}
59
+ ensure_transition_action_valid!(virtual_machine.state, state_to_set)
60
+ options[:mode] = :gui if state_to_set == :start && options[:mode].nil?
61
+ state_set_successfully = case state_to_set
62
+ when :start then
63
+ logger.debug("Backend starting VM (#{virtual_machine.uuid}/#{virtual_machine.name})")
64
+ virtual_machine.start(options[:mode])
65
+ when :pause then
66
+ logger.debug("Backend pausing VM (#{virtual_machine.uuid}/#{virtual_machine.name})")
67
+ virtual_machine.pause
68
+ #FIXME: evil hack, this method doesn't seem to block until completed
69
+ sleep(VM_BACKEND_SETTLE_TIME)
70
+ virtual_machine.paused?
71
+ when :saved_state then
72
+ logger.debug("Backend saving state of VM (#{virtual_machine.uuid}/#{virtual_machine.name})")
73
+ virtual_machine.save_state
74
+ when :stop then
75
+ logger.debug("Backend stopping VM (#{virtual_machine.uuid}/#{virtual_machine.name})")
76
+ virtual_machine.stop
77
+ #FIXME: evil hack, this method doesn't seem to block until completed
78
+ sleep(VM_BACKEND_SETTLE_TIME)
79
+ virtual_machine.powered_off?
80
+ when :shutdown then
81
+ logger.debug("Backend shutting down VM (#{virtual_machine.uuid}/#{virtual_machine.name})")
82
+ virtual_machine.shutdown
83
+ #FIXME: this command should not fail, but the guest operating system won't neccessarily shut down immediately. this is a safe shutdown
84
+ true
85
+ when :resume then
86
+ logger.debug("Backend resuming VM (#{virtual_machine.uuid}/#{virtual_machine.name})")
87
+ virtual_machine.resume
88
+ #FIXME: evil hack, this method doesn't seem to block until completed
89
+ sleep(VM_BACKEND_SETTLE_TIME)
90
+ virtual_machine.running?
91
+ end
92
+ raise Errors::StateTransitionError, "Error transitioning VM to state #{state_to_set}. Virtual Machine backend thinks machine is now in state: #{virtual_machine.state}" unless state_set_successfully
93
+ end
94
+
95
+ def can_transition_to_state? current_state, action
96
+ action_set = VALID_ACTION_TO_STATE_APPLICATION_MAP[action]
97
+ !action_set.nil? && action_set.member?(current_state)
98
+ end
99
+
100
+ private
101
+
102
+ def ensure_transition_action_valid! current_state, action
103
+ message = VALID_ACTION_TO_STATE_APPLICATION_MAP[action].nil? ? "Setting state to #{action} not implemented" : "Machine not in one of (#{VALID_ACTION_TO_STATE_APPLICATION_MAP[action] }) states. Cannot #{action}"
104
+ raise Errors::InvalidStateError, message unless can_transition_to_state? current_state, action
105
+ end
106
+
107
+ def ensure_valid_backend_version backend
108
+ version = backend.version
109
+ logger.debug("VirtualBox backend version: #{version}")
110
+ raise Errors::BackendUnavailableError, "Backend could not be loaded" if version.nil?
111
+ true
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+ end
118
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: virtuoso-ensure-state
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31098201
5
+ prerelease: true
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 6
10
+ - beta
11
+ version: 0.0.6.beta
12
+ platform: ruby
13
+ authors:
14
+ - 3Crowd Technologies, Inc.
15
+ - Justin Lynn
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2010-11-10 00:00:00 -08:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: virtualbox
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ hash: 9
32
+ segments:
33
+ - 0
34
+ - 7
35
+ - 5
36
+ version: 0.7.5
37
+ type: :runtime
38
+ version_requirements: *id001
39
+ description: Ensures the state of virtual machines on the host system. If a Virtual Machine is not in the desired state, report it, and, if desired, take action to put them in that state
40
+ email:
41
+ - eng@3crowd.com
42
+ executables:
43
+ - virtuoso-ensure-state
44
+ extensions: []
45
+
46
+ extra_rdoc_files: []
47
+
48
+ files:
49
+ - bin/virtuoso-ensure-state
50
+ - lib/ensure-state.rb
51
+ - lib/ensure-state/version.rb
52
+ - lib/ensure-state/vm.rb
53
+ - lib/ensure-state/cli/option_parser.rb
54
+ - lib/ensure-state/cli.rb
55
+ - lib/ensure-state/engine.rb
56
+ - lib/ensure-state/vm_backend/base.rb
57
+ - lib/ensure-state/vm_backend/virtualbox.rb
58
+ - lib/ensure-state/errors/ensure_state_error.rb
59
+ - lib/ensure-state/errors/vm_backend_errors/backend_unavailable_error.rb
60
+ - lib/ensure-state/errors/vm_backend_errors/invalid_state_error.rb
61
+ - lib/ensure-state/errors/vm_backend_errors/state_transition_error.rb
62
+ - lib/ensure-state/errors/vm_backend_errors/vm_not_found_error.rb
63
+ - lib/ensure-state/errors/invalid_vm_backend_error.rb
64
+ - lib/ensure-state/errors/vm_backend_error.rb
65
+ - LICENSE
66
+ - README
67
+ - CHANGELOG
68
+ has_rdoc: true
69
+ homepage: http://github.com/3Crowd/virtuoso-ensure-state
70
+ licenses: []
71
+
72
+ post_install_message:
73
+ rdoc_options: []
74
+
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ hash: 3
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 23
92
+ segments:
93
+ - 1
94
+ - 3
95
+ - 6
96
+ version: 1.3.6
97
+ requirements: []
98
+
99
+ rubyforge_project:
100
+ rubygems_version: 1.3.7
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Ensures the state of virtual machines on the host system
104
+ test_files: []
105
+