fission 0.4.0 → 0.5.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +12 -3
- data/README.md +45 -19
- data/bin/fission +1 -1
- data/fission.gemspec +3 -2
- data/lib/fission.rb +15 -0
- data/lib/fission/action/shell_executor.rb +37 -0
- data/lib/fission/action/snapshot/creator.rb +81 -0
- data/lib/fission/action/snapshot/deleter.rb +85 -0
- data/lib/fission/action/snapshot/lister.rb +90 -0
- data/lib/fission/action/snapshot/reverter.rb +81 -0
- data/lib/fission/action/vm/cloner.rb +191 -0
- data/lib/fission/action/vm/deleter.rb +73 -0
- data/lib/fission/action/vm/lister.rb +138 -0
- data/lib/fission/action/vm/starter.rb +88 -0
- data/lib/fission/action/vm/stopper.rb +79 -0
- data/lib/fission/action/vm/suspender.rb +68 -0
- data/lib/fission/cli.rb +21 -117
- data/lib/fission/command.rb +39 -0
- data/lib/fission/command/clone.rb +11 -6
- data/lib/fission/command/delete.rb +11 -6
- data/lib/fission/command/info.rb +62 -0
- data/lib/fission/command/snapshot_create.rb +9 -3
- data/lib/fission/command/snapshot_delete.rb +38 -0
- data/lib/fission/command/snapshot_list.rb +9 -3
- data/lib/fission/command/snapshot_revert.rb +9 -3
- data/lib/fission/command/start.rb +11 -6
- data/lib/fission/command/status.rb +9 -1
- data/lib/fission/command/stop.rb +9 -3
- data/lib/fission/command/suspend.rb +11 -6
- data/lib/fission/command_helpers.rb +18 -4
- data/lib/fission/command_line_parser.rb +189 -0
- data/lib/fission/config.rb +1 -1
- data/lib/fission/fusion.rb +3 -4
- data/lib/fission/lease.rb +2 -1
- data/lib/fission/metadata.rb +6 -1
- data/lib/fission/response.rb +17 -9
- data/lib/fission/version.rb +1 -1
- data/lib/fission/vm.rb +142 -382
- data/lib/fission/vm_configuration.rb +79 -0
- data/spec/fission/action/execute_shell_command_spec.rb +25 -0
- data/spec/fission/action/snapshot/creator_spec.rb +77 -0
- data/spec/fission/action/snapshot/deleter_spec.rb +84 -0
- data/spec/fission/action/snapshot/lister_spec.rb +67 -0
- data/spec/fission/action/snapshot/reverter_spec.rb +76 -0
- data/spec/fission/action/vm/cloner_spec.rb +198 -0
- data/spec/fission/action/vm/deleter_spec.rb +79 -0
- data/spec/fission/action/vm/lister_spec.rb +164 -0
- data/spec/fission/action/vm/starter_spec.rb +88 -0
- data/spec/fission/action/vm/stopper_spec.rb +71 -0
- data/spec/fission/action/vm/suspender_spec.rb +59 -0
- data/spec/fission/cli_spec.rb +32 -157
- data/spec/fission/command/clone_spec.rb +9 -3
- data/spec/fission/command/delete_spec.rb +11 -3
- data/spec/fission/command/info_spec.rb +130 -0
- data/spec/fission/command/snapshot_create_spec.rb +11 -3
- data/spec/fission/command/snapshot_delete_spec.rb +74 -0
- data/spec/fission/command/snapshot_list_spec.rb +11 -3
- data/spec/fission/command/snapshot_revert_spec.rb +11 -3
- data/spec/fission/command/start_spec.rb +11 -3
- data/spec/fission/command/status_spec.rb +16 -5
- data/spec/fission/command/stop_spec.rb +11 -3
- data/spec/fission/command/suspend_spec.rb +11 -3
- data/spec/fission/command_helpers_spec.rb +27 -5
- data/spec/fission/command_line_parser_spec.rb +267 -0
- data/spec/fission/command_spec.rb +16 -1
- data/spec/fission/config_spec.rb +3 -3
- data/spec/fission/fusion_spec.rb +11 -6
- data/spec/fission/lease_spec.rb +81 -45
- data/spec/fission/metadata_spec.rb +29 -6
- data/spec/fission/response_spec.rb +20 -9
- data/spec/fission/ui_spec.rb +1 -1
- data/spec/fission/vm_configuration_spec.rb +132 -0
- data/spec/fission/vm_spec.rb +393 -750
- data/spec/helpers/command_helpers.rb +1 -1
- metadata +93 -15
- data/.rvmrc +0 -1
@@ -0,0 +1,189 @@
|
|
1
|
+
module Fission
|
2
|
+
|
3
|
+
class CommandLineParser
|
4
|
+
|
5
|
+
# Internal: Creates a new Fission::CommandLineParser object.
|
6
|
+
#
|
7
|
+
# args - The command line arguments to parse. This is expected to be in the
|
8
|
+
# same format of ARGV (Array) (default: ARGV).
|
9
|
+
#
|
10
|
+
# Examples:
|
11
|
+
#
|
12
|
+
# CommandLineParser.new ['foo', 'bar']
|
13
|
+
#
|
14
|
+
# CommandLineParser.new
|
15
|
+
#
|
16
|
+
# Returns a Fission::CommandLineParser object.
|
17
|
+
def initialize(args=ARGV)
|
18
|
+
@args = args
|
19
|
+
|
20
|
+
gather_commands_and_summaries
|
21
|
+
|
22
|
+
setup_options_parser
|
23
|
+
end
|
24
|
+
|
25
|
+
# Internal: Parses the command line arguments. If the arguments are
|
26
|
+
# invalid, the appropriate help will be output and then will exit. If the
|
27
|
+
# arguments are valid, then the @command variable will be set to a new
|
28
|
+
# instance of the determined command class.
|
29
|
+
#
|
30
|
+
# Examples:
|
31
|
+
#
|
32
|
+
# @command_line_parser.parse
|
33
|
+
#
|
34
|
+
# Returns nothing.
|
35
|
+
def parse
|
36
|
+
@options_parser.order! @args
|
37
|
+
|
38
|
+
determine_command_provided
|
39
|
+
self
|
40
|
+
rescue OptionParser::InvalidOption => e
|
41
|
+
ui.output e
|
42
|
+
show_all_help
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
|
46
|
+
# Internal: Accessor for an instance of the determined command. This is set
|
47
|
+
# by running the parse method.
|
48
|
+
#
|
49
|
+
# Examples:
|
50
|
+
#
|
51
|
+
# @command_line_parser.command
|
52
|
+
#
|
53
|
+
# Returns an instance of the determined command if the arguments are valid.
|
54
|
+
# Returns nil if the parse command has not yet been run.
|
55
|
+
def command
|
56
|
+
@command
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
# Internal: Sets up the base option parser.
|
61
|
+
#
|
62
|
+
# Examples:
|
63
|
+
#
|
64
|
+
# @cli.setup_option_parser
|
65
|
+
#
|
66
|
+
# Returns nothing. This will set the @option_parser instance variable.
|
67
|
+
def setup_options_parser
|
68
|
+
@options_parser = OptionParser.new do |opts|
|
69
|
+
opts.banner = "\nUsage: fission [options] COMMAND [arguments]"
|
70
|
+
|
71
|
+
opts.on_head('-v', '--version', 'Output the version of fission') do
|
72
|
+
ui.output VERSION
|
73
|
+
exit 0
|
74
|
+
end
|
75
|
+
|
76
|
+
opts.on_head('-h', '--help', 'Displays this message') do
|
77
|
+
show_all_help
|
78
|
+
exit 0
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Internal: Provides the help of all of the known commands.
|
84
|
+
#
|
85
|
+
# Examples
|
86
|
+
#
|
87
|
+
# @cli.commands_help
|
88
|
+
#
|
89
|
+
# Outputs the summary text for all known commands.
|
90
|
+
def commands_help
|
91
|
+
longest_cmd = @commands.inject do |longest, cmd_name|
|
92
|
+
longest.length > cmd_name.length ? longest : cmd_name
|
93
|
+
end
|
94
|
+
|
95
|
+
ui.output "\nCommands:"
|
96
|
+
|
97
|
+
Hash[@command_names_and_summaries.sort].each_pair do |name, summary|
|
98
|
+
ui.output_printf "%-#{longest_cmd.length}s %s\n", name, summary
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Internal: Determines all of the available commands and their summaries.
|
103
|
+
#
|
104
|
+
# Examples
|
105
|
+
#
|
106
|
+
# @cli.command_names_and_summaries
|
107
|
+
# # => { 'clone' => 'Clones a VM', 'stop' => 'Stops a VM' }
|
108
|
+
#
|
109
|
+
# Returns nothing. This will set the @command_names_and_summaries instance
|
110
|
+
# variable with the result.
|
111
|
+
def gather_commands_and_summaries
|
112
|
+
@command_names_and_summaries = Command.descendants.inject({}) do |result, klass|
|
113
|
+
cmd = klass.new
|
114
|
+
result[cmd.command_name] = cmd.summary
|
115
|
+
result
|
116
|
+
end
|
117
|
+
|
118
|
+
@commands = @command_names_and_summaries.keys.sort
|
119
|
+
end
|
120
|
+
|
121
|
+
# Internal: Determines the command that has been provided. If it is
|
122
|
+
# determined that an invalid command is provided, then the help/usage will
|
123
|
+
# be displayed and it will exit.
|
124
|
+
#
|
125
|
+
# Examples:
|
126
|
+
#
|
127
|
+
# @cli.determine_command_to_execute
|
128
|
+
#
|
129
|
+
# Returns nothing. This will set the @command instance variable to an instance
|
130
|
+
# of the appropriate command class (assuming it is valid).
|
131
|
+
def determine_command_provided
|
132
|
+
if @commands.include? @args.first
|
133
|
+
@command = Command.const_get(@args.first.capitalize).new @args.drop 1
|
134
|
+
elsif is_snapshot_command?
|
135
|
+
klass = @args.take(2).map {|c| c.capitalize}.join('')
|
136
|
+
@command = Command.const_get(klass).new @args.drop 2
|
137
|
+
else
|
138
|
+
show_all_help
|
139
|
+
exit 1
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Internal: Determines if the provided command is a snapshot related
|
144
|
+
# command. This will use the @args instance variable to make the
|
145
|
+
# determination.
|
146
|
+
#
|
147
|
+
# Examples
|
148
|
+
#
|
149
|
+
# @cli.is_snapshot_command? ['foo', 'bar']
|
150
|
+
# # => false
|
151
|
+
#
|
152
|
+
# @cli.is_snapshot_command? ['snapshot', 'list']
|
153
|
+
# # => true
|
154
|
+
#
|
155
|
+
# Returns a Boolean of whether a snapshot command was given or not.
|
156
|
+
def is_snapshot_command?
|
157
|
+
@args.first == 'snapshot' &&
|
158
|
+
@args.count > 1 &&
|
159
|
+
@commands.include?(@args.take(2).join(' '))
|
160
|
+
end
|
161
|
+
|
162
|
+
# Internal: Outputs the usage as well as the known commands and their
|
163
|
+
# summaries.
|
164
|
+
#
|
165
|
+
# Examples
|
166
|
+
#
|
167
|
+
# @cli.show_all_help
|
168
|
+
# # => 'fission options command arguments ....'
|
169
|
+
#
|
170
|
+
# Returns nothing.
|
171
|
+
def show_all_help
|
172
|
+
ui.output @options_parser
|
173
|
+
commands_help
|
174
|
+
end
|
175
|
+
|
176
|
+
# Internal: Helper method for outputting text to the ui
|
177
|
+
#
|
178
|
+
# Examples
|
179
|
+
#
|
180
|
+
# @cli.ui.output 'foo'
|
181
|
+
#
|
182
|
+
# Returns a UI instance.
|
183
|
+
def ui
|
184
|
+
@ui ||= UI.new
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
data/lib/fission/config.rb
CHANGED
@@ -27,7 +27,7 @@ module Fission
|
|
27
27
|
|
28
28
|
load_from_file
|
29
29
|
|
30
|
-
@attributes['vmrun_cmd'] = "#{@attributes['vmrun_bin']
|
30
|
+
@attributes['vmrun_cmd'] = "'#{@attributes['vmrun_bin']}' -T fusion"
|
31
31
|
end
|
32
32
|
|
33
33
|
# Public: Helper method to access config atributes. This is a shortcut for
|
data/lib/fission/fusion.rb
CHANGED
@@ -11,10 +11,9 @@ module Fission
|
|
11
11
|
# Returns a Boolean.
|
12
12
|
def self.running?
|
13
13
|
command = "ps -ef | grep -v grep | grep -c "
|
14
|
-
command << "#{Fission.config['gui_bin']
|
15
|
-
|
16
|
-
|
17
|
-
output.strip.to_i > 0
|
14
|
+
command << "'#{Fission.config['gui_bin']}' 2>&1"
|
15
|
+
command_executor = Fission::Action::ShellExecutor.new command
|
16
|
+
command_executor.execute['output'].strip.to_i > 0
|
18
17
|
end
|
19
18
|
|
20
19
|
end
|
data/lib/fission/lease.rb
CHANGED
@@ -97,7 +97,8 @@ module Fission
|
|
97
97
|
|
98
98
|
if all_response.successful?
|
99
99
|
response = Response.new :code => 0
|
100
|
-
|
100
|
+
leases = all_response.data.find_all { |l| l.mac_address == mac_address }
|
101
|
+
response.data = leases.sort_by { |l| l.end }.last
|
101
102
|
else
|
102
103
|
response = all_response
|
103
104
|
end
|
data/lib/fission/metadata.rb
CHANGED
@@ -85,7 +85,12 @@ module Fission
|
|
85
85
|
#
|
86
86
|
# Returns nothing.
|
87
87
|
def delete_vm_favorite_entry(vm_path)
|
88
|
-
@content
|
88
|
+
if @content.has_key?('VMFavoritesListDefaults2')
|
89
|
+
@content['VMFavoritesListDefaults2'].delete_if { |vm| vm['path'] == vm_path }
|
90
|
+
end
|
91
|
+
if @content.has_key?('fusionInitialSessions')
|
92
|
+
@content['fusionInitialSessions'].delete_if {|vm| vm['documentPath'] == vm_path}
|
93
|
+
end
|
89
94
|
end
|
90
95
|
|
91
96
|
end
|
data/lib/fission/response.rb
CHANGED
@@ -57,18 +57,26 @@ module Fission
|
|
57
57
|
@code == 0
|
58
58
|
end
|
59
59
|
|
60
|
-
#
|
61
|
-
# command
|
60
|
+
# Internal: Helper method to create a new Response object when using
|
61
|
+
# executing a command through Fission::Action::ShellExecutor.
|
62
62
|
#
|
63
|
-
#
|
63
|
+
# shell_execute_result - This should be the result of running 'execute' on
|
64
|
+
# a ShellExecutor object.
|
65
|
+
#
|
66
|
+
# Examples:
|
67
|
+
#
|
68
|
+
# Response.from_shell_executor shell_execute_result
|
64
69
|
#
|
65
70
|
# Returns a Response.
|
66
|
-
# The
|
67
|
-
#
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
response
|
71
|
+
# The code attribute of the Response will be set to the exit_status
|
72
|
+
# attribute of the provided process_status data. The message attribute of
|
73
|
+
# the Response will be set to the output of the provided data if, and only
|
74
|
+
# if, the Response is unsuccessful.
|
75
|
+
def self.from_shell_executor(shell_execute_result)
|
76
|
+
response = new :code => shell_execute_result['process_status'].exitstatus
|
77
|
+
unless response.successful?
|
78
|
+
response.message = shell_execute_result['output']
|
79
|
+
end
|
72
80
|
response
|
73
81
|
end
|
74
82
|
|
data/lib/fission/version.rb
CHANGED
data/lib/fission/vm.rb
CHANGED
@@ -21,33 +21,23 @@ module Fission
|
|
21
21
|
# If successful, the Response's data attribute will be nil.
|
22
22
|
# If there is an error, an unsuccessful Response will be returned.
|
23
23
|
def create_snapshot(name)
|
24
|
-
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
running_response = running?
|
29
|
-
return running_response unless running_response.successful?
|
30
|
-
|
31
|
-
unless running_response.data
|
32
|
-
message = 'The VM must be running in order to take a snapshot.'
|
33
|
-
return Response.new :code => 1, :message => message
|
34
|
-
end
|
35
|
-
|
36
|
-
conf_file_response = conf_file
|
37
|
-
return conf_file_response unless conf_file_response.successful?
|
38
|
-
|
39
|
-
snapshots_response = snapshots
|
40
|
-
return snapshots_response unless snapshots_response.successful?
|
41
|
-
|
42
|
-
if snapshots_response.data.include? name
|
43
|
-
message = "There is already a snapshot named '#{name}'."
|
44
|
-
return Response.new :code => 1, :message => message
|
45
|
-
end
|
46
|
-
|
47
|
-
command = "#{vmrun_cmd} snapshot "
|
48
|
-
command << "#{conf_file_response.data} \"#{name}\" 2>&1"
|
24
|
+
Fission::Action::Snapshot::Creator.new(self).create_snapshot(name)
|
25
|
+
end
|
49
26
|
|
50
|
-
|
27
|
+
# Public: Deletes a snapshot for a VM. The snapshot to delete must exist.
|
28
|
+
# If the Fusion GUI is running, then the VM must also be running.
|
29
|
+
#
|
30
|
+
# name - The name of the snapshot to delete.
|
31
|
+
#
|
32
|
+
# Examples
|
33
|
+
#
|
34
|
+
# @vm.delete_snapshot('foo_snap_1')
|
35
|
+
#
|
36
|
+
# Returns a Response with the result.
|
37
|
+
# If successful, the Response's data attribute will be nil.
|
38
|
+
# If there is an error, an unsuccessful Response will be returned.
|
39
|
+
def delete_snapshot(name)
|
40
|
+
Fission::Action::Snapshot::Deleter.new(self).delete_snapshot(name)
|
51
41
|
end
|
52
42
|
|
53
43
|
# Public: Reverts the VM to the specified snapshot. The snapshot to revert
|
@@ -63,32 +53,7 @@ module Fission
|
|
63
53
|
# If successful, the Response's data attribute will be nil.
|
64
54
|
# If there is an error, an unsuccessful Response will be returned.
|
65
55
|
def revert_to_snapshot(name)
|
66
|
-
|
67
|
-
return Response.new :code => 1, :message => 'VM does not exist'
|
68
|
-
end
|
69
|
-
|
70
|
-
if Fusion.running?
|
71
|
-
message = 'It looks like the Fusion GUI is currently running. '
|
72
|
-
message << 'A VM cannot be reverted to a snapshot when the Fusion GUI is running. '
|
73
|
-
message << 'Exit the Fusion GUI and try again.'
|
74
|
-
return Response.new :code => 1, :message => message
|
75
|
-
end
|
76
|
-
|
77
|
-
conf_file_response = conf_file
|
78
|
-
return conf_file_response unless conf_file_response.successful?
|
79
|
-
|
80
|
-
snapshots_response = snapshots
|
81
|
-
return snapshots_response unless snapshots_response.successful?
|
82
|
-
|
83
|
-
unless snapshots_response.data.include? name
|
84
|
-
message = "Unable to find a snapshot named '#{name}'."
|
85
|
-
return Response.new :code => 1, :message => message
|
86
|
-
end
|
87
|
-
|
88
|
-
command = "#{vmrun_cmd} revertToSnapshot "
|
89
|
-
command << "#{conf_file_response.data} \"#{name}\" 2>&1"
|
90
|
-
|
91
|
-
Response.from_command(`#{command}`)
|
56
|
+
Fission::Action::Snapshot::Reverter.new(self).revert_to_snapshot(name)
|
92
57
|
end
|
93
58
|
|
94
59
|
# Public: List the snapshots for a VM.
|
@@ -103,27 +68,28 @@ module Fission
|
|
103
68
|
# snapshot names (String).
|
104
69
|
# If there is an error, an unsuccessful Response will be returned.
|
105
70
|
def snapshots
|
106
|
-
|
107
|
-
|
108
|
-
end
|
109
|
-
|
110
|
-
conf_file_response = conf_file
|
111
|
-
return conf_file_response unless conf_file_response.successful?
|
112
|
-
|
113
|
-
command = "#{vmrun_cmd} listSnapshots "
|
114
|
-
command << "#{conf_file_response.data} 2>&1"
|
115
|
-
output = `#{command}`
|
116
|
-
|
117
|
-
response = Response.new :code => $?.exitstatus
|
118
|
-
|
119
|
-
if response.successful?
|
120
|
-
snaps = output.split("\n").select { |s| !s.include? 'Total snapshots:' }
|
121
|
-
response.data = snaps.map { |s| s.strip }
|
122
|
-
else
|
123
|
-
response.message = output
|
124
|
-
end
|
71
|
+
Fission::Action::Snapshot::Lister.new(self).snapshots
|
72
|
+
end
|
125
73
|
|
126
|
-
|
74
|
+
# Public: Deletes a VM. The VM must not be running in order to delete it.
|
75
|
+
# As there are a number issues with the Fusion command line tool for
|
76
|
+
# deleting VMs, this is a best effort. The VM must not be running when this
|
77
|
+
# method is called. This essentially deletes the VM directory and attempts
|
78
|
+
# to remove the relevant entries from the Fusion plist file. It's highly
|
79
|
+
# recommended to delete VMs without the Fusion GUI running. If the Fusion
|
80
|
+
# GUI is running this method should succeed, but it's been observed that
|
81
|
+
# Fusion will recreate the plist data which is deleted. This leads to
|
82
|
+
# 'missing' VMs in the Fusion GUI.
|
83
|
+
#
|
84
|
+
# Examples
|
85
|
+
#
|
86
|
+
# @vm.delete
|
87
|
+
#
|
88
|
+
# Returns a Response with the result.
|
89
|
+
# If successful, the Response's data attribute will be nil.
|
90
|
+
# If there is an error, an unsuccessful Response will be returned.
|
91
|
+
def delete
|
92
|
+
Fission::Action::VM::Deleter.new(self).delete
|
127
93
|
end
|
128
94
|
|
129
95
|
# Public: Starts a VM. The VM must not be running in order to start it.
|
@@ -144,36 +110,7 @@ module Fission
|
|
144
110
|
# If successful, the Response's data attribute will be nil.
|
145
111
|
# If there is an error, an unsuccessful Response will be returned.
|
146
112
|
def start(options={})
|
147
|
-
|
148
|
-
return Response.new :code => 1, :message => 'VM does not exist'
|
149
|
-
end
|
150
|
-
|
151
|
-
running_response = running?
|
152
|
-
return running_response unless running_response.successful?
|
153
|
-
|
154
|
-
if running_response.data
|
155
|
-
return Response.new :code => 1, :message => 'VM is already running'
|
156
|
-
end
|
157
|
-
|
158
|
-
conf_file_response = conf_file
|
159
|
-
return conf_file_response unless conf_file_response.successful?
|
160
|
-
|
161
|
-
unless options[:headless].blank?
|
162
|
-
if Fusion.running?
|
163
|
-
message = 'It looks like the Fusion GUI is currently running. '
|
164
|
-
message << 'A VM cannot be started in headless mode when the Fusion GUI is running. '
|
165
|
-
message << 'Exit the Fusion GUI and try again.'
|
166
|
-
return Response.new :code => 1, :message => message
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
command = "#{vmrun_cmd} start "
|
171
|
-
command << "#{conf_file_response.data} "
|
172
|
-
|
173
|
-
command << (options[:headless].blank? ? 'gui ' : 'nogui ')
|
174
|
-
command << '2>&1'
|
175
|
-
|
176
|
-
Response.from_command(`#{command}`)
|
113
|
+
Fission::Action::VM::Starter.new(self).start(options)
|
177
114
|
end
|
178
115
|
|
179
116
|
# Public: Stops a VM. The VM must be running in order to stop it.
|
@@ -195,26 +132,7 @@ module Fission
|
|
195
132
|
# If successful, the Response's data attribute will be nil.
|
196
133
|
# If there is an error, an unsuccessful Response will be returned.
|
197
134
|
def stop(options={})
|
198
|
-
|
199
|
-
return Response.new :code => 1, :message => 'VM does not exist'
|
200
|
-
end
|
201
|
-
|
202
|
-
running_response = running?
|
203
|
-
return running_response unless running_response.successful?
|
204
|
-
|
205
|
-
unless running_response.data
|
206
|
-
return Response.new :code => 1, :message => 'VM is not running'
|
207
|
-
end
|
208
|
-
|
209
|
-
conf_file_response = conf_file
|
210
|
-
return conf_file_response unless conf_file_response.successful?
|
211
|
-
|
212
|
-
command = "#{vmrun_cmd} stop "
|
213
|
-
command << "#{conf_file_response.data} "
|
214
|
-
command << 'hard ' unless options[:hard].blank?
|
215
|
-
command << '2>&1'
|
216
|
-
|
217
|
-
Response.from_command(`#{command}`)
|
135
|
+
Fission::Action::VM::Stopper.new(self).stop(options)
|
218
136
|
end
|
219
137
|
|
220
138
|
# Public: Suspends a VM. The VM must be running in order to suspend it.
|
@@ -227,24 +145,35 @@ module Fission
|
|
227
145
|
# If successful, the Response's data attribute will be nil.
|
228
146
|
# If there is an error, an unsuccessful Response will be returned.
|
229
147
|
def suspend
|
230
|
-
|
231
|
-
|
232
|
-
end
|
148
|
+
Fission::Action::VM::Suspender.new(self).suspend
|
149
|
+
end
|
233
150
|
|
234
|
-
|
235
|
-
|
151
|
+
# Public: Provides virtual hardware info the VM
|
152
|
+
#
|
153
|
+
# Examples:
|
154
|
+
#
|
155
|
+
# @vm.hardware_info.data
|
156
|
+
# # => {'cpus' => 2, 'memory' => 1024}
|
157
|
+
#
|
158
|
+
# Returns a Response with the result.
|
159
|
+
# If successful, the Response's data attribute will be a Hash with the
|
160
|
+
# info found.
|
161
|
+
# If there is an error, an unsuccessful Response will be returned.
|
162
|
+
def hardware_info
|
163
|
+
config_response = conf_file_data
|
164
|
+
return config_response unless config_response.successful?
|
236
165
|
|
237
|
-
|
238
|
-
return Response.new :code => 1, :message => 'VM is not running'
|
239
|
-
end
|
166
|
+
response = Response.new :code => 0, :data => {}
|
240
167
|
|
241
|
-
|
242
|
-
return conf_file_response unless conf_file_response.successful?
|
168
|
+
response.data['cpus'] = 1
|
243
169
|
|
244
|
-
|
245
|
-
|
170
|
+
{ 'cpus' => 'numvcpus', 'memory' => 'memsize' }.each_pair do |k,v|
|
171
|
+
unless config_response.data[v].blank?
|
172
|
+
response.data[k] = config_response.data[v].to_i
|
173
|
+
end
|
174
|
+
end
|
246
175
|
|
247
|
-
|
176
|
+
response
|
248
177
|
end
|
249
178
|
|
250
179
|
# Public: Provides the MAC addresses for a VM.
|
@@ -297,30 +226,76 @@ module Fission
|
|
297
226
|
# attribute will be an empty Hash.
|
298
227
|
# If there is an error, an unsuccessful Response will be returned.
|
299
228
|
def network_info
|
300
|
-
|
301
|
-
return
|
229
|
+
config_response = conf_file_data
|
230
|
+
return config_response unless config_response.successful?
|
302
231
|
|
303
232
|
response = Response.new :code => 0, :data => {}
|
304
233
|
|
305
234
|
interface_pattern = /^ethernet\d+/
|
306
235
|
mac_pattern = /(\w\w[:-]\w\w[:-]\w\w[:-]\w\w[:-]\w\w[:-]\w\w)/
|
307
236
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
response.data[int] = {}
|
313
|
-
response.data[int]['mac_address'] = mac
|
314
|
-
|
237
|
+
config_response.data.each_pair do |k,v|
|
238
|
+
if v =~ mac_pattern
|
239
|
+
mac = v
|
240
|
+
int = k.scan(interface_pattern).first
|
241
|
+
response.data[int] = { 'mac_address' => mac }
|
315
242
|
lease_response = Fission::Lease.find_by_mac_address mac
|
316
243
|
return lease_response unless lease_response.successful?
|
317
244
|
|
318
245
|
response.data[int]['ip_address'] = nil
|
319
|
-
|
320
246
|
if lease_response.data
|
321
247
|
response.data[int]['ip_address'] = lease_response.data.ip_address
|
322
248
|
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
response
|
253
|
+
end
|
254
|
+
|
255
|
+
# Public: Provides the Fussion GuestOS profile.
|
256
|
+
#
|
257
|
+
# Examples
|
258
|
+
#
|
259
|
+
# @vm.guestos.data
|
260
|
+
# # => 'debian5'
|
261
|
+
#
|
262
|
+
# Returns a Response with the result.
|
263
|
+
# If the Response is successful, the Response's data attribute will
|
264
|
+
# be populated with a string that is the guest operatingsystem used for the
|
265
|
+
# virtual machine.
|
266
|
+
# If there is an error, an unsuccessful Response will be returned.
|
267
|
+
def guestos
|
268
|
+
config_response = conf_file_data
|
269
|
+
return config_response unless config_response.successful?
|
323
270
|
|
271
|
+
response = Response.new :code => 0, :data => {}
|
272
|
+
response.data = config_response.data.fetch 'guestOS', ''
|
273
|
+
response
|
274
|
+
end
|
275
|
+
|
276
|
+
# Public: Provides various uuids associated with a VM.
|
277
|
+
#
|
278
|
+
# Examples
|
279
|
+
#
|
280
|
+
# @vm.uuids.data
|
281
|
+
# # => {"bios"=>"56 4d ee 72 3b 7e 47 67-69 aa 65 cb 5e 40 3f 21",
|
282
|
+
# "location"=>"56 4d 2e 15 f4 ed 00 a7-c5 99 43 32 b8 76 ef d5"}
|
283
|
+
#
|
284
|
+
# Returns a Response with the result.
|
285
|
+
# If the Response is successful, the Response's data attribute will
|
286
|
+
# be populated with a hash that is comprised of the various uuids that are
|
287
|
+
# associated with each VM. If the VM is newly created they will be the same
|
288
|
+
# but if you selected 'I Moved It' from the Fusion GUI they will differ.
|
289
|
+
# If there is an error, an unsuccessful Response will be returned.
|
290
|
+
def uuids
|
291
|
+
config_response = conf_file_data
|
292
|
+
return config_response unless config_response.successful?
|
293
|
+
|
294
|
+
response = Response.new :code => 0, :data => {}
|
295
|
+
|
296
|
+
{ 'bios' => 'uuid.bios', 'location' => 'uuid.location' }.each_pair do |k,v|
|
297
|
+
unless config_response.data[v].blank?
|
298
|
+
response.data[k] = config_response.data[v]
|
324
299
|
end
|
325
300
|
end
|
326
301
|
|
@@ -439,6 +414,22 @@ module Fission
|
|
439
414
|
response
|
440
415
|
end
|
441
416
|
|
417
|
+
# Public: Provides the configuration data stored in the VM's configuration
|
418
|
+
# file ('.vmx').
|
419
|
+
#
|
420
|
+
# Examples
|
421
|
+
#
|
422
|
+
# @vm.conf_file_data
|
423
|
+
#
|
424
|
+
# Returns a Response with the result.
|
425
|
+
# If successful, the Response's data attribute with be a Hash of the
|
426
|
+
# configuration data. All of the keys/values in the configuration data will
|
427
|
+
# be a String.
|
428
|
+
# If there is an error, an unsuccessful Response will be returned.
|
429
|
+
def conf_file_data
|
430
|
+
VMConfiguration.new(self).config_data
|
431
|
+
end
|
432
|
+
|
442
433
|
# Public: Determines the path to the VM's config file ('.vmx').
|
443
434
|
#
|
444
435
|
# Examples
|
@@ -485,8 +476,6 @@ module Fission
|
|
485
476
|
end
|
486
477
|
end
|
487
478
|
|
488
|
-
response.data.gsub! ' ', '\ ' if response.successful?
|
489
|
-
|
490
479
|
response
|
491
480
|
end
|
492
481
|
|
@@ -519,14 +508,7 @@ module Fission
|
|
519
508
|
# empty Array.
|
520
509
|
# If there is an error, an unsuccessful Response will be returned.
|
521
510
|
def self.all
|
522
|
-
|
523
|
-
File.directory? d
|
524
|
-
end
|
525
|
-
|
526
|
-
response = Response.new :code => 0
|
527
|
-
response.data = vm_dirs.collect { |d| new(File.basename d, '.vmwarevm') }
|
528
|
-
|
529
|
-
response
|
511
|
+
Fission::Action::VM::Lister.new.all
|
530
512
|
end
|
531
513
|
|
532
514
|
# Public: Provides all of the VMs which are currently running.
|
@@ -543,25 +525,7 @@ module Fission
|
|
543
525
|
# attribute will be an empty Array.
|
544
526
|
# If there is an error, an unsuccessful Response will be returned.
|
545
527
|
def self.all_running
|
546
|
-
|
547
|
-
|
548
|
-
output = `#{command}`
|
549
|
-
|
550
|
-
response = Response.new :code => $?.exitstatus
|
551
|
-
|
552
|
-
if response.successful?
|
553
|
-
vms = output.split("\n").select do |vm|
|
554
|
-
vm.include?('.vmx') && File.exists?(vm) && File.extname(vm) == '.vmx'
|
555
|
-
end
|
556
|
-
|
557
|
-
response.data = vms.collect do |vm|
|
558
|
-
new File.basename(File.dirname(vm), '.vmwarevm')
|
559
|
-
end
|
560
|
-
else
|
561
|
-
response.message = output
|
562
|
-
end
|
563
|
-
|
564
|
-
response
|
528
|
+
Fission::Action::VM::Lister.new.all_running
|
565
529
|
end
|
566
530
|
|
567
531
|
# Public: Provides a list of all of the VMs and their associated status
|
@@ -576,34 +540,7 @@ module Fission
|
|
576
540
|
# names as keys and their status as the values.
|
577
541
|
# If there is an error, an unsuccessful Repsonse will be returned.
|
578
542
|
def self.all_with_status
|
579
|
-
|
580
|
-
return all_response unless all_response.successful?
|
581
|
-
|
582
|
-
all_vms = all_response.data
|
583
|
-
|
584
|
-
running_response = all_running
|
585
|
-
return running_response unless running_response.successful?
|
586
|
-
|
587
|
-
response = Response.new :code => 0
|
588
|
-
|
589
|
-
all_running_vm_names = running_response.data.collect { |v| v.name }
|
590
|
-
|
591
|
-
response.data = all_vms.inject({}) do |result, vm|
|
592
|
-
if all_running_vm_names.include? vm.name
|
593
|
-
status = 'running'
|
594
|
-
else
|
595
|
-
if vm.suspend_file_exists?
|
596
|
-
status = 'suspended'
|
597
|
-
else
|
598
|
-
status = 'not running'
|
599
|
-
end
|
600
|
-
end
|
601
|
-
|
602
|
-
result[vm.name] = status
|
603
|
-
result
|
604
|
-
end
|
605
|
-
|
606
|
-
response
|
543
|
+
Fission::Action::VM::Lister.new.all_with_status
|
607
544
|
end
|
608
545
|
|
609
546
|
# Public: Creates a new VM which is a clone of an existing VM. As Fusion
|
@@ -622,189 +559,12 @@ module Fission
|
|
622
559
|
# If successful, the Response's data attribute will be nil.
|
623
560
|
# If there is an error, an unsuccessful Response will be returned.
|
624
561
|
def self.clone(source_vm_name, target_vm_name)
|
625
|
-
source_vm = new source_vm_name
|
626
|
-
target_vm = new target_vm_name
|
627
|
-
|
628
|
-
unless source_vm.exists?
|
629
|
-
return Response.new :code => 1, :message => 'VM does not exist'
|
630
|
-
end
|
631
|
-
|
632
|
-
if target_vm.exists?
|
633
|
-
return Response.new :code => 1, :message => 'VM already exists'
|
634
|
-
end
|
635
|
-
|
636
|
-
FileUtils.cp_r source_vm.path, target_vm.path
|
637
|
-
|
638
|
-
rename_vm_files source_vm.name, target_vm.name
|
639
|
-
update_config source_vm.name, target_vm.name
|
640
|
-
|
641
|
-
Response.new :code => 0
|
642
|
-
end
|
643
|
-
|
644
|
-
# Public: Deletes a VM. The VM must not be running in order to delete it.
|
645
|
-
# As there are a number issues with the Fusion command line tool for
|
646
|
-
# deleting VMs, this is a best effort. The VM must not be running when this
|
647
|
-
# method is called. This essentially deletes the VM directory and attempts
|
648
|
-
# to remove the relevant entries from the Fusion plist file. It's highly
|
649
|
-
# recommended to delete VMs without the Fusion GUI running. If the Fusion
|
650
|
-
# GUI is running this method should succeed, but it's been observed that
|
651
|
-
# Fusion will recreate the plist data which is deleted. This leads to
|
652
|
-
# 'missing' VMs in the Fusion GUI.
|
653
|
-
#
|
654
|
-
# Examples
|
655
|
-
#
|
656
|
-
# @vm.delete
|
657
|
-
#
|
658
|
-
# Returns a Response with the result.
|
659
|
-
# If successful, the Response's data attribute will be nil.
|
660
|
-
# If there is an error, an unsuccessful Response will be returned.
|
661
|
-
def delete
|
662
|
-
unless exists?
|
663
|
-
return Response.new :code => 1, :message => 'VM does not exist'
|
664
|
-
end
|
665
|
-
|
666
|
-
running_response = running?
|
667
|
-
return running_response unless running_response.successful?
|
668
|
-
|
669
|
-
if running_response.data
|
670
|
-
message = 'The VM must not be running in order to delete it.'
|
671
|
-
return Response.new :code => 1, :message => message
|
672
|
-
end
|
673
|
-
|
674
|
-
FileUtils.rm_rf path
|
675
|
-
Metadata.delete_vm_info path
|
676
|
-
|
677
|
-
Response.new :code => 0
|
562
|
+
@source_vm = new source_vm_name
|
563
|
+
@target_vm = new target_vm_name
|
564
|
+
Fission::Action::VM::Cloner.new(@source_vm, @target_vm).clone
|
678
565
|
end
|
679
566
|
|
680
567
|
private
|
681
|
-
# Internal: Renames the files of a newly cloned VM.
|
682
|
-
#
|
683
|
-
# from - The VM name that was used as the source of the clone.
|
684
|
-
# to - The name of the newly cloned VM.
|
685
|
-
#
|
686
|
-
# Examples
|
687
|
-
#
|
688
|
-
# Fission::VM.rename_vm_files 'foo', 'bar'
|
689
|
-
#
|
690
|
-
# Returns nothing.
|
691
|
-
def self.rename_vm_files(from, to)
|
692
|
-
to_vm = new to
|
693
|
-
|
694
|
-
files_to_rename(from, to).each do |file|
|
695
|
-
text_to_replace = File.basename(file, File.extname(file))
|
696
|
-
|
697
|
-
if File.extname(file) == '.vmdk'
|
698
|
-
if file.match /\-s\d+\.vmdk/
|
699
|
-
text_to_replace = file.partition(/\-s\d+.vmdk/).first
|
700
|
-
end
|
701
|
-
end
|
702
|
-
|
703
|
-
unless File.exists?(File.join(to_vm.path, file.gsub(text_to_replace, to)))
|
704
|
-
FileUtils.mv File.join(to_vm.path, file),
|
705
|
-
File.join(to_vm.path, file.gsub(text_to_replace, to))
|
706
|
-
end
|
707
|
-
end
|
708
|
-
end
|
709
|
-
|
710
|
-
# Internal: Provides the list of files which need to be renamed in a newly
|
711
|
-
# cloned VM directory.
|
712
|
-
#
|
713
|
-
# from - The VM name that was used as the source of the clone.
|
714
|
-
# to - The name of the newly cloned VM.
|
715
|
-
#
|
716
|
-
# Examples
|
717
|
-
#
|
718
|
-
# Fission::VM.files_to_rename 'foo', 'bar'
|
719
|
-
# # => ['/vms/vm1/foo.vmdk', '/vms/vm1/foo.vmx', 'vms/vm1/blah.other']
|
720
|
-
#
|
721
|
-
# Returns an Array containing the paths (String) to the files to rename.
|
722
|
-
# The paths which match the from name will preceed any other files found in
|
723
|
-
# the newly cloned VM directory.
|
724
|
-
def self.files_to_rename(from, to)
|
725
|
-
to_vm = new to
|
726
|
-
|
727
|
-
files_which_match_source_vm = []
|
728
|
-
other_files = []
|
729
|
-
|
730
|
-
Dir.entries(to_vm.path).each do |f|
|
731
|
-
unless f == '.' || f == '..'
|
732
|
-
f.include?(from) ? files_which_match_source_vm << f : other_files << f
|
733
|
-
end
|
734
|
-
end
|
735
|
-
|
736
|
-
files_which_match_source_vm + other_files
|
737
|
-
end
|
738
|
-
|
739
|
-
# Internal: Provides the list of file extensions for VM related files.
|
740
|
-
#
|
741
|
-
# Examples
|
742
|
-
#
|
743
|
-
# Fission::VM.vm_file_extension
|
744
|
-
# # => ['.nvram', '.vmdk', '.vmem']
|
745
|
-
#
|
746
|
-
# Returns an Array containing the file extensions of VM realted files.
|
747
|
-
# The file extensions returned are Strings and include a '.'.
|
748
|
-
def self.vm_file_extensions
|
749
|
-
['.nvram', '.vmdk', '.vmem', '.vmsd', '.vmss', '.vmx', '.vmxf']
|
750
|
-
end
|
751
|
-
|
752
|
-
# Internal: Updates config files for a newly cloned VM. This will update any
|
753
|
-
# files with the extension of '.vmx', '.vmxf', and '.vmdk'. Any binary
|
754
|
-
# '.vmdk' files will be skipped.
|
755
|
-
#
|
756
|
-
# from - The VM name that was used as the source of the clone.
|
757
|
-
# to - The name of the newly cloned VM.
|
758
|
-
#
|
759
|
-
# Examples
|
760
|
-
#
|
761
|
-
# Fission::VM.update_config 'foo', 'bar'
|
762
|
-
#
|
763
|
-
# Returns nothing.
|
764
|
-
def self.update_config(from, to)
|
765
|
-
to_vm = new to
|
766
|
-
|
767
|
-
['.vmx', '.vmxf', '.vmdk'].each do |ext|
|
768
|
-
file = File.join to_vm.path, "#{to}#{ext}"
|
769
|
-
|
770
|
-
unless File.binary?(file)
|
771
|
-
text = (File.read file).gsub from, to
|
772
|
-
File.open(file, 'w'){ |f| f.print text }
|
773
|
-
end
|
774
|
-
|
775
|
-
clean_up_conf_file(file) if ext == '.vmx'
|
776
|
-
end
|
777
|
-
end
|
778
|
-
|
779
|
-
# Internal: Cleans up the conf file (*.vmx) for a newly cloned VM. This
|
780
|
-
# includes removing generated MAC addresses, setting up for a new UUID, and
|
781
|
-
# disable VMware tools warning.
|
782
|
-
#
|
783
|
-
# conf_file_path - Aboslute path to the VM's conf file (.vmx).
|
784
|
-
#
|
785
|
-
# Examples
|
786
|
-
#
|
787
|
-
# VM.clean_up_conf_file '/vms/foo/foo.vmx'
|
788
|
-
#
|
789
|
-
# Returns nothing.
|
790
|
-
def self.clean_up_conf_file(conf_file_path)
|
791
|
-
conf_items_patterns = [ /^tools\.remindInstall.*\n/,
|
792
|
-
/^uuid\.action.*\n/,
|
793
|
-
/^ethernet\.+generatedAddress.*\n/ ]
|
794
|
-
|
795
|
-
content = File.read conf_file_path
|
796
|
-
|
797
|
-
conf_items_patterns.each do |pattern|
|
798
|
-
content.gsub(pattern, '').strip
|
799
|
-
end
|
800
|
-
|
801
|
-
content << "\n"
|
802
|
-
content << "tools.remindInstall = \"FALSE\"\n"
|
803
|
-
content << "uuid.action = \"create\"\n"
|
804
|
-
|
805
|
-
File.open(conf_file_path, 'w') { |f| f.print content }
|
806
|
-
end
|
807
|
-
|
808
568
|
# Internal: Helper for getting the configured vmrun_cmd value.
|
809
569
|
#
|
810
570
|
# Examples
|