virtuoso-ensure-state 0.0.6.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+