CloudyScripts 0.0.11 → 0.0.12
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/LICENSE +7 -7
- data/README.rdoc +27 -27
- data/Rakefile +50 -50
- data/lib/cloudyscripts.rb +26 -26
- data/lib/help/dm_crypt_helper.rb +53 -54
- data/lib/help/ec2_helper.rb +54 -54
- data/lib/help/remote_command_handler.rb +213 -203
- data/lib/help/script_execution_state.rb +108 -97
- data/lib/help/state_change_listener.rb +13 -13
- data/lib/help/state_transition_helper.rb +362 -0
- data/lib/scripts/ec2/ami2_ebs_conversion.rb +180 -477
- data/lib/scripts/ec2/dm_encrypt.rb +147 -205
- data/lib/scripts/ec2/download_snapshot.rb +153 -0
- data/lib/scripts/ec2/ec2_script.rb +101 -52
- metadata +4 -2
@@ -1,97 +1,108 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
@
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
1
|
+
require "help/state_transition_helper"
|
2
|
+
|
3
|
+
# Implements a little state-machine.
|
4
|
+
# Usage: for every state you need, extend this class.
|
5
|
+
# The method enter() must be implemented for every state you code and
|
6
|
+
# return another state.
|
7
|
+
class ScriptExecutionState
|
8
|
+
include StateTransitionHelper
|
9
|
+
|
10
|
+
# context information for the state (hash)
|
11
|
+
attr_reader :context, :logger
|
12
|
+
|
13
|
+
def initialize(context)
|
14
|
+
@context = context
|
15
|
+
@state_change_listeners = []
|
16
|
+
@logger = context[:logger]
|
17
|
+
if @logger == nil
|
18
|
+
@logger = Logger.new(STDOUT)
|
19
|
+
@logger.level = Logger::WARN
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Listener should extend #StateChangeListener (or implement a
|
24
|
+
# state_changed(state) method). Note: calls are synchronous.
|
25
|
+
def register_state_change_listener(listener)
|
26
|
+
@state_change_listeners << listener
|
27
|
+
end
|
28
|
+
|
29
|
+
# Start the state machine using this state as initial state.
|
30
|
+
def start_state_machine
|
31
|
+
@current_state = self
|
32
|
+
@logger.info "start state machine with #{@current_state.to_s}"
|
33
|
+
while !@current_state.done? && !@current_state.failed?
|
34
|
+
begin
|
35
|
+
@logger.info "state machine: current state = #{@current_state.to_s}"
|
36
|
+
@current_state = @current_state.enter()
|
37
|
+
notify_state_change_listeners(@current_state)
|
38
|
+
rescue Exception => e
|
39
|
+
if @context[:result] != nil
|
40
|
+
@context[:result][:details] = e.backtrace().join("\n")
|
41
|
+
end
|
42
|
+
@current_state = FailedState.new(@context, e.to_s, @current_state)
|
43
|
+
notify_state_change_listeners(@current_state)
|
44
|
+
@logger.warn "StateMachine exception during execution: #{e}"
|
45
|
+
@logger.warn "#{e.backtrace.join("\n")}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@current_state
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the state that is reached after execution.
|
52
|
+
def end_state
|
53
|
+
@current_state
|
54
|
+
end
|
55
|
+
|
56
|
+
# To be implemented. Executes the code for this state.
|
57
|
+
def enter
|
58
|
+
raise Exception.new("TaskExecutionState is abstract")
|
59
|
+
end
|
60
|
+
|
61
|
+
# To be implemented. Indicates if the final state is reached.
|
62
|
+
def done?
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
# To be implemented. Indicates if the final state is a failure state.
|
67
|
+
def failed?
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
s = self.class.to_s
|
73
|
+
s.sub(/.*\:\:/,'')
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Notifies all listeners of state changes
|
79
|
+
def notify_state_change_listeners(state)
|
80
|
+
@state_change_listeners.each() {|listener|
|
81
|
+
listener.state_changed(state)
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
# Standard state reached when an exception occurs.
|
88
|
+
class FailedState < ScriptExecutionState
|
89
|
+
attr_accessor :failure_reason, :from_state
|
90
|
+
def initialize(context, failure_reason, from_state)
|
91
|
+
super(context)
|
92
|
+
@failure_reason = failure_reason
|
93
|
+
@from_state = from_state
|
94
|
+
end
|
95
|
+
def done?
|
96
|
+
true
|
97
|
+
end
|
98
|
+
def failed?
|
99
|
+
true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Done.
|
104
|
+
class Done < ScriptExecutionState
|
105
|
+
def done?
|
106
|
+
true
|
107
|
+
end
|
108
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
|
-
# Defines a template for a class that allows to be notified
|
2
|
-
# on state-changes in #ScriptExecutionState. A listener must be
|
3
|
-
# registered via #ScriptExecutionState::register_state_change_listener.
|
4
|
-
# When a state changes the method #state_changed is called.
|
5
|
-
|
6
|
-
class StateChangeListener
|
7
|
-
# Method called when a state changes. Note: calls are synchronous, the
|
8
|
-
# listener should return quickly and handle more complicated routines
|
9
|
-
# in a different thread.
|
10
|
-
def state_changed(state)
|
11
|
-
raise Exception.new("StateChangeListener: state change notification not implemented")
|
12
|
-
end
|
13
|
-
end
|
1
|
+
# Defines a template for a class that allows to be notified
|
2
|
+
# on state-changes in #ScriptExecutionState. A listener must be
|
3
|
+
# registered via #ScriptExecutionState::register_state_change_listener.
|
4
|
+
# When a state changes the method #state_changed is called.
|
5
|
+
|
6
|
+
class StateChangeListener
|
7
|
+
# Method called when a state changes. Note: calls are synchronous, the
|
8
|
+
# listener should return quickly and handle more complicated routines
|
9
|
+
# in a different thread.
|
10
|
+
def state_changed(state)
|
11
|
+
raise Exception.new("StateChangeListener: state change notification not implemented")
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,362 @@
|
|
1
|
+
# Contains methods that are used by the scripts in the state-machines. Since
|
2
|
+
# they are reused by different scripts, they are factored into this module.
|
3
|
+
# Parameters are read from the @context variable that must be defined. Results
|
4
|
+
# are written into @context[:result][...]
|
5
|
+
# Note: @context[:script] is set to a script object to pass information and messages
|
6
|
+
# to listeners
|
7
|
+
|
8
|
+
module StateTransitionHelper
|
9
|
+
|
10
|
+
# Connects to the remote host via SSH.
|
11
|
+
# Params in @context:
|
12
|
+
# * :dns_name => machine to connect to
|
13
|
+
# * :ssh_keyfile => key-file used for ssh OR :ssh_keydata => contents of key-file
|
14
|
+
# * :remote_command_handler => ssh wrapper object
|
15
|
+
def connect
|
16
|
+
@context[:script].post_message("connecting to #{@context[:dns_name]}...")
|
17
|
+
if @context[:remote_command_handler] == nil
|
18
|
+
@context[:remote_command_handler] = RemoteCommandHandler.new
|
19
|
+
end
|
20
|
+
connected = false
|
21
|
+
remaining_trials = 3
|
22
|
+
while !connected && remaining_trials > 0
|
23
|
+
remaining_trials -= 1
|
24
|
+
if @context[:ssh_keyfile] != nil
|
25
|
+
begin
|
26
|
+
@context[:remote_command_handler].connect_with_keyfile(@context[:dns_name], @context[:ssh_keyfile])
|
27
|
+
connected = true
|
28
|
+
rescue Exception => e
|
29
|
+
@logger.info("connection failed due to #{e}")
|
30
|
+
@logger.debug(e.backtrace.join("\n"))
|
31
|
+
end
|
32
|
+
elsif @context[:ssh_keydata] != nil
|
33
|
+
begin
|
34
|
+
@context[:remote_command_handler].connect(@context[:dns_name], "root", @context[:ssh_keydata])
|
35
|
+
connected = true
|
36
|
+
rescue Exception => e
|
37
|
+
@logger.info("connection failed due to #{e}")
|
38
|
+
@logger.debug(e.backtrace.join("\n"))
|
39
|
+
end
|
40
|
+
else
|
41
|
+
raise Exception.new("no key information specified")
|
42
|
+
end
|
43
|
+
if !connected
|
44
|
+
sleep(5) #try again
|
45
|
+
end
|
46
|
+
end
|
47
|
+
if !connected
|
48
|
+
raise Exception.new("connection attempts stopped")
|
49
|
+
end
|
50
|
+
@context[:result][:os] = @context[:remote_command_handler].retrieve_os()
|
51
|
+
@context[:script].post_message("connected to #{@context[:dns_name]}. OS installed is #{@context[:result][:os]}")
|
52
|
+
@logger.info "connected to #{@context[:dns_name]}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Launch an instance based on an AMI ID
|
56
|
+
# Input Parameters in @context:
|
57
|
+
# * :ami_id => ID of the AMI to be launched
|
58
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
59
|
+
# * :key_name => name of the key to access the instance
|
60
|
+
# * :security_group_name => name of the security group to be used
|
61
|
+
# Output information set by this method in @context:
|
62
|
+
# * :instance_id => ID of the started instance
|
63
|
+
# * :dns_name => DNS name of the started instance
|
64
|
+
# * :availability_zone => Availability zone of the started instance
|
65
|
+
# * :kernel_id => EC2 Kernel ID of the started instance
|
66
|
+
# * :ramdisk_id => EC2 Ramdisk ID of the started instance
|
67
|
+
# * :architecture => architecture (e.g. 386i, 64x) of the started instance
|
68
|
+
def launch_instance
|
69
|
+
@context[:script].post_message("starting up instance to execute the script (AMI = #{@context[:ami_id]}) ...")
|
70
|
+
@logger.debug "start up AMI #{@context[:ami_id]}"
|
71
|
+
res = @context[:ec2_api_handler].run_instances(:image_id => @context[:ami_id],
|
72
|
+
:security_group => @context[:security_group_name], :key_name => @context[:key_name])
|
73
|
+
instance_id = res['instancesSet']['item'][0]['instanceId']
|
74
|
+
@context[:instance_id] = instance_id
|
75
|
+
@logger.info "started instance #{instance_id}"
|
76
|
+
@context[:script].post_message("started instance #{instance_id}. wait until it is ready...")
|
77
|
+
#availability_zone , key_name/group_name
|
78
|
+
started = false
|
79
|
+
while started == false
|
80
|
+
sleep(5)
|
81
|
+
res = @context[:ec2_api_handler].describe_instances(:instance_id => @context[:instance_id])
|
82
|
+
state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
|
83
|
+
@logger.info "instance is in state #{state['name']} (#{state['code']})"
|
84
|
+
if state['code'].to_i == 16
|
85
|
+
started = true
|
86
|
+
@context[:script].post_message("instance is up and running")
|
87
|
+
@context[:dns_name] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['dnsName']
|
88
|
+
@context[:availability_zone] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['placement']['availabilityZone']
|
89
|
+
@context[:kernel_id] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['kernelId']
|
90
|
+
@context[:ramdisk_id] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['ramdiskId']
|
91
|
+
@context[:architecture] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['architecture']
|
92
|
+
elsif state['code'].to_i != 0
|
93
|
+
@context[:script].post_message("instance in state #{state['name']}")
|
94
|
+
raise Exception.new('instance failed to start up')
|
95
|
+
else
|
96
|
+
@context[:script].post_message("instance still starting up...")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Shuts down an instance.
|
102
|
+
# Input Parameters in @context:
|
103
|
+
# * :instance_id => ID of the instance to be shut down
|
104
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
105
|
+
def shut_down_instance()
|
106
|
+
@context[:script].post_message("going to shut down the temporary instance #{@context[:instance_id]}...")
|
107
|
+
@logger.debug "shutdown instance #{@context[:instance_id]}"
|
108
|
+
res = @context[:ec2_api_handler].terminate_instances(:instance_id => @context[:instance_id])
|
109
|
+
done = false
|
110
|
+
while done == false
|
111
|
+
sleep(5)
|
112
|
+
res = @context[:ec2_api_handler].describe_instances(:instance_id => @context[:instance_id])
|
113
|
+
state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
|
114
|
+
@logger.debug "instance in state #{state['name']} (#{state['code']})"
|
115
|
+
if state['code'].to_i == 48
|
116
|
+
done = true
|
117
|
+
elsif state['code'].to_i != 32
|
118
|
+
raise Exception.new('instance failed to shut down')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
@context[:script].post_message("instance #{@context[:instance_id]} is terminated")
|
122
|
+
end
|
123
|
+
|
124
|
+
# Creates a new EBS volume.
|
125
|
+
# Input Parameters in @context:
|
126
|
+
# * :availability_zone => availability zone for the volume
|
127
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
128
|
+
# Output information set by this method in @context:
|
129
|
+
# * :volume_id => EC2 EBS Volume ID
|
130
|
+
def create_volume()
|
131
|
+
@context[:script].post_message("going to create a new EBS volume...")
|
132
|
+
@logger.debug "create volume in zone #{@context[:availability_zone]}"
|
133
|
+
res = @context[:ec2_api_handler].create_volume(:availability_zone => @context[:availability_zone], :size => "10")
|
134
|
+
@context[:volume_id] = res['volumeId']
|
135
|
+
started = false
|
136
|
+
while !started
|
137
|
+
sleep(5)
|
138
|
+
#TODO: check for timeout?
|
139
|
+
res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
|
140
|
+
state = res['volumeSet']['item'][0]['status']
|
141
|
+
@logger.debug "volume state #{state}"
|
142
|
+
if state == 'available'
|
143
|
+
started = true
|
144
|
+
end
|
145
|
+
end
|
146
|
+
@context[:script].post_message("EBS volume #{@context[:volume_id]} is ready")
|
147
|
+
end
|
148
|
+
|
149
|
+
# Creates a new EBS volume from a snapshot ID.
|
150
|
+
# Input Parameters in @context:
|
151
|
+
# * :availability_zone => availability zone for the volume
|
152
|
+
# * :snapshot_id => EC2 Snapshot ID used to create the volume
|
153
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
154
|
+
# Output information set by this method in @context:
|
155
|
+
# * :volume_id => EC2 EBS Volume ID created
|
156
|
+
def create_volume_from_snapshot
|
157
|
+
@context[:script].post_message("going to create a new EBS volume from the specified snapshot...")
|
158
|
+
@logger.debug "create volume in zone #{@context[:availability_zone]}"
|
159
|
+
res = @context[:ec2_api_handler].create_volume(:snapshot_id => @context[:snapshot_id], :availability_zone => @context[:availability_zone], :size => "10")
|
160
|
+
@context[:volume_id] = res['volumeId']
|
161
|
+
started = false
|
162
|
+
while !started
|
163
|
+
sleep(5)
|
164
|
+
#TODO: check for timeout?
|
165
|
+
res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
|
166
|
+
state = res['volumeSet']['item'][0]['status']
|
167
|
+
@logger.debug "volume state #{state}"
|
168
|
+
if state == 'available'
|
169
|
+
started = true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
@context[:script].post_message("EBS volume #{@context[:volume_id]} is ready")
|
173
|
+
end
|
174
|
+
|
175
|
+
# Attaches an EBS volume to an instance
|
176
|
+
# Input Parameters in @context:
|
177
|
+
# * :volume_id => EC2 ID for the EBS Volume to be attached
|
178
|
+
# * :instance_id => EC2 ID for the instance to which the volume is supposed to be attached to
|
179
|
+
# * :temp_device_name => device name to be used for attaching (e.g. /dev/sdj1)
|
180
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
181
|
+
def attach_volume
|
182
|
+
@context[:script].post_message("going to attach volume #{@context[:volume_id]} to instance #{@context[:instance_id]} on device #{@context[:temp_device_name]}...")
|
183
|
+
@logger.debug "attach volume #{@context[:volume_id]} to instance #{@context[:instance_id]} on device #{@context[:temp_device_name]}"
|
184
|
+
@context[:ec2_api_handler].attach_volume(:volume_id => @context[:volume_id],
|
185
|
+
:instance_id => @context[:instance_id],
|
186
|
+
:device => @context[:temp_device_name]
|
187
|
+
)
|
188
|
+
done = false
|
189
|
+
while !done
|
190
|
+
sleep(5)
|
191
|
+
#TODO: check for timeout?
|
192
|
+
res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
|
193
|
+
state = res['volumeSet']['item'][0]['status']
|
194
|
+
@logger.debug "storage attaching: #{state}"
|
195
|
+
if state == 'in-use'
|
196
|
+
done = true
|
197
|
+
end
|
198
|
+
end
|
199
|
+
@context[:script].post_message("volume successfully attached")
|
200
|
+
end
|
201
|
+
|
202
|
+
# Detach an EBS volume from an instance.
|
203
|
+
# Input Parameters in @context:
|
204
|
+
# * :volume_id => EC2 ID for the EBS Volume to be detached
|
205
|
+
# * :instance_id => EC2 ID for the instance to detach from
|
206
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
207
|
+
def detach_volume()
|
208
|
+
@context[:script].post_message("going to detach volume #{@context[:volume_id]}...")
|
209
|
+
@logger.debug "detach volume #{@context[:volume_id]}"
|
210
|
+
@context[:ec2_api_handler].detach_volume(:volume_id => @context[:volume_id],
|
211
|
+
:instance_id => @context[:instance_id]
|
212
|
+
)
|
213
|
+
done = false
|
214
|
+
while !done
|
215
|
+
sleep(3)
|
216
|
+
#TODO: check for timeout?
|
217
|
+
res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
|
218
|
+
@logger.debug "volume detaching: #{res.inspect}"
|
219
|
+
if res['volumeSet']['item'][0]['status'] == 'available'
|
220
|
+
done = true
|
221
|
+
end
|
222
|
+
end
|
223
|
+
@context[:script].post_message("volume #{@context[:volume_id]} detached.")
|
224
|
+
end
|
225
|
+
|
226
|
+
# Delete an EBS volume.
|
227
|
+
# Input Parameters in @context:
|
228
|
+
# * :volume_id => EC2 ID for the EBS Volume to be deleted
|
229
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
230
|
+
def delete_volume
|
231
|
+
@context[:script].post_message("going to delete volume #{@context[:volume_id]} (no longer needed)...")
|
232
|
+
@logger.debug "delete volume #{@context[:volume_id]}"
|
233
|
+
@context[:ec2_api_handler].delete_volume(:volume_id => @context[:volume_id])
|
234
|
+
@context[:script].post_message("volume #{@context[:volume_id]} deleted")
|
235
|
+
end
|
236
|
+
|
237
|
+
# Creates a snapshot for an EBS volume.
|
238
|
+
# Input Parameters in @context:
|
239
|
+
# * :volume_id => EC2 ID for the EBS volume to be snapshotted
|
240
|
+
# * :snapshot_id => EC2 Snapshot ID used to create the volume
|
241
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
242
|
+
# Output information set by this method in @context:
|
243
|
+
# * :snapshot_id => EC2 ID for the snapshot created
|
244
|
+
def create_snapshot()
|
245
|
+
@context[:script].post_message("going to create a snapshot...")
|
246
|
+
@logger.debug "create snapshot for volume #{@context[:volume_id]}"
|
247
|
+
res = @context[:ec2_api_handler].create_snapshot(:volume_id => @context[:volume_id])
|
248
|
+
@context[:snapshot_id] = res['snapshotId']
|
249
|
+
@logger.info "snapshot_id = #{@context[:snapshot_id]}"
|
250
|
+
done = false
|
251
|
+
while !done
|
252
|
+
sleep(5)
|
253
|
+
#TODO: check for timeout?
|
254
|
+
res = @context[:ec2_api_handler].describe_snapshots(:snapshot_id => @context[:snapshot_id])
|
255
|
+
@logger.debug "snapshot creating: #{res.inspect}"
|
256
|
+
if res['snapshotSet']['item'][0]['status'] == 'completed'
|
257
|
+
done = true
|
258
|
+
end
|
259
|
+
end
|
260
|
+
@context[:script].post_message("snapshot is done with ID=#{@context[:snapshot_id]}")
|
261
|
+
end
|
262
|
+
|
263
|
+
# Registers a snapshot as EBS-booted AMI.
|
264
|
+
# Input Parameters in @context:
|
265
|
+
# * :snapshot_id => EC2 Snapshot ID used to be used
|
266
|
+
# * :name => name of the AMI to be created
|
267
|
+
# * :root_device_name => Root device name (e.g. /dev/sdj) to be used for AMI registration
|
268
|
+
# * :description => description of the AMI to be created
|
269
|
+
# * :kernel_id => EC2 Kernel ID to be used for AMI registration
|
270
|
+
# * :ramdisk_id => EC2 Ramdisk ID to be used for AMI registration
|
271
|
+
# * :architecture => architecture (e.g. 386i, 64x) to be used for AMI registration
|
272
|
+
# * :ec2_api_handler => wrapper object around EC2 API access
|
273
|
+
# Output information set by this method in @context:
|
274
|
+
# * {:result => :image_id} => ID of the AMI created and registered
|
275
|
+
def register_snapshot()
|
276
|
+
@context[:script].post_message("going to register snapshot #{@context[:snapshot_id]}...")
|
277
|
+
@logger.debug "register snapshot #{@context[:snapshot_id]} as #{@context[:name]}"
|
278
|
+
res = @context[:ec2_api_handler].register_image_updated(:snapshot_id => @context[:snapshot_id],
|
279
|
+
:kernel_id => @context[:kernel_id], :architecture => @context[:architecture],
|
280
|
+
:root_device_name => @context[:root_device_name],
|
281
|
+
:description => @context[:description], :name => @context[:name],
|
282
|
+
:ramdisk_id => @context[:ramdisk_id]
|
283
|
+
)
|
284
|
+
@logger.debug "result of registration = #{res.inspect}"
|
285
|
+
@context[:result][:image_id] = res['imageId']
|
286
|
+
@logger.info "resulting image_id = #{@context[:result][:image_id]}"
|
287
|
+
@context[:script].post_message("snapshot #{@context[:snapshot_id]} successfully registered as AMI #{@context[:result][:image_id]} ")
|
288
|
+
end
|
289
|
+
|
290
|
+
# Create a file-system on a given machine (assumes to be connected already).
|
291
|
+
# Input Parameters in @context:
|
292
|
+
# * :dns_name => IP used
|
293
|
+
# * :temp_device_name => device name to be used #TODO: give it a better name
|
294
|
+
# * :remote_command_handler => ssh wrapper object
|
295
|
+
def create_fs()
|
296
|
+
@context[:script].post_message("going to create filesystem on #{@context[:dns_name]} to #{@context[:temp_device_name]}...")
|
297
|
+
@logger.debug "create filesystem on #{@context[:dns_name]} to #{@context[:temp_device_name]}"
|
298
|
+
@context[:remote_command_handler].create_filesystem("ext3", @context[:temp_device_name])
|
299
|
+
@context[:script].post_message("filesystem system successfully created")
|
300
|
+
end
|
301
|
+
|
302
|
+
# Create a file-system on a given machine (assumes to be connected already).
|
303
|
+
# Input Parameters in @context:
|
304
|
+
# * :path => mount point #TODO: give it a better name
|
305
|
+
# * :temp_device_name => device used for mounting #TODO: give it a better name
|
306
|
+
# * :remote_command_handler => ssh wrapper object
|
307
|
+
def mount_fs()
|
308
|
+
@context[:path] = "/mnt/tmp_#{@context[:volume_id]}"
|
309
|
+
@context[:script].post_message("going to mount #{@context[:temp_device_name]} on #{@context[:path]}...")
|
310
|
+
@logger.debug "mount #{@context[:temp_device_name]} on #{@context[:path]}"
|
311
|
+
@context[:remote_command_handler].mkdir(@context[:path])
|
312
|
+
@context[:remote_command_handler].mount(@context[:temp_device_name], @context[:path])
|
313
|
+
sleep(2) #give mount some time
|
314
|
+
if !@context[:remote_command_handler].drive_mounted?(@context[:path])
|
315
|
+
raise Exception.new("drive #{@context[:path]} not mounted")
|
316
|
+
end
|
317
|
+
@context[:script].post_message("mount successful")
|
318
|
+
end
|
319
|
+
|
320
|
+
# Unmount a drive
|
321
|
+
# Input Parameters in @context:
|
322
|
+
# * :path => mount point #TODO: give it a better name
|
323
|
+
# * :remote_command_handler => ssh wrapper object
|
324
|
+
def unmount_fs()
|
325
|
+
@context[:script].post_message("Going to unmount ...")
|
326
|
+
@logger.debug "unmount #{@context[:path]}"
|
327
|
+
@context[:remote_command_handler].umount(@context[:path])
|
328
|
+
sleep(2) #give umount some time
|
329
|
+
if @context[:remote_command_handler].drive_mounted?(@context[:path])
|
330
|
+
raise Exception.new("drive #{@context[:path]} not unmounted")
|
331
|
+
end
|
332
|
+
@context[:script].post_message("device unmounted")
|
333
|
+
end
|
334
|
+
|
335
|
+
# Copy all files of a running linux distribution via rsync to a mounted directory
|
336
|
+
# Input Parameters in @context:
|
337
|
+
# * :path => where to copy to
|
338
|
+
# * :remote_command_handler => ssh wrapper object
|
339
|
+
def copy()
|
340
|
+
@context[:script].post_message("going to start copying files to #{@context[:path]}. This may take quite a time...")
|
341
|
+
@logger.debug "start copying to #{@context[:path]}"
|
342
|
+
start = Time.new.to_i
|
343
|
+
@context[:remote_command_handler].rsync("/", "#{@context[:path]}", "/mnt/")
|
344
|
+
@context[:remote_command_handler].rsync("/dev/", "#{@context[:path]}/dev/")
|
345
|
+
endtime = Time.new.to_i
|
346
|
+
@logger.info "copy took #{(endtime-start)}s"
|
347
|
+
@context[:script].post_message("copying is done (took #{endtime-start})s")
|
348
|
+
end
|
349
|
+
|
350
|
+
# Zips all files on a mounted-directory into a file
|
351
|
+
# Input Parameters in @context:
|
352
|
+
# * :path => where to copy from
|
353
|
+
# * :zip_file_dest => path where the zip-file should be stored
|
354
|
+
# # :zip_file_name => name of the zip file (without .zip suffix)
|
355
|
+
# * :remote_command_handler => ssh wrapper object
|
356
|
+
def zip_volume
|
357
|
+
@context[:script].post_message("going to zip the EBS volume")
|
358
|
+
@context[:remote_command_handler].zip(@context[:path], @context[:zip_file_dest]+@context[:zip_file_name])
|
359
|
+
@context[:script].post_message("EBS volume successfully zipped")
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|