CloudyScripts 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Matthias Jung
1
+ Copyright (c) 2010 SecludIT (http://secludit.com)
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ require 'rake/testtask'
12
12
 
13
13
  spec = Gem::Specification.new do |s|
14
14
  s.name = 'CloudyScripts'
15
- s.version = '0.0.13'
15
+ s.version = '0.0.14'
16
16
  s.has_rdoc = true
17
17
  s.extra_rdoc_files = ['README.rdoc', 'LICENSE']
18
18
  s.summary = 'Scripts to facilitate programming for infrastructure clouds.'
@@ -51,4 +51,12 @@ class Ec2Helper
51
51
  }
52
52
  return false
53
53
  end
54
+
55
+ def volume_prop(volume_id, prop)
56
+ vols = @ec2_api.describe_volumes(:volume_id => volume_id)
57
+ if vols['volumeSet']['item'].size == 0
58
+ raise Exception.new("volume #{volume_id} not found")
59
+ end
60
+ return vols['volumeSet']['item'][0][prop.to_s]
61
+ end
54
62
  end
@@ -12,8 +12,8 @@ class RemoteCommandHandler
12
12
  # Params:
13
13
  # * ip: ip address of the machine to connect to
14
14
  # * keyfile: path of the keyfile to be used for authentication
15
- def connect_with_keyfile(ip, keyfile)
16
- @ssh_session = Net::SSH.start(ip, 'root', :keys => [keyfile])
15
+ def connect_with_keyfile(ip, keyfile, timeout = 30)
16
+ @ssh_session = Net::SSH.start(ip, 'root', {:keys => [keyfile], :timeout => timeout})
17
17
  end
18
18
 
19
19
  # Connect to the machine as root using keydata from a keyfile.
@@ -21,8 +21,8 @@ class RemoteCommandHandler
21
21
  # * ip: ip address of the machine to connect to
22
22
  # * user: user name
23
23
  # * key_data: key_data to be used for authentication
24
- def connect(ip, user, key_data)
25
- @ssh_session = Net::SSH.start(ip, user, :key_data => [key_data])
24
+ def connect(ip, user, key_data, timeout = 30)
25
+ @ssh_session = Net::SSH.start(ip, user, {:key_data => [key_data], :timeout => timeout})
26
26
  end
27
27
 
28
28
  # Disconnect the current handler
@@ -115,7 +115,7 @@ class RemoteCommandHandler
115
115
  # Zip the complete contents of the source path into the destination file.
116
116
  def zip(source_path, destination_file)
117
117
  begin
118
- exec = "cd #{source_path}; zip #{destination_file} *"
118
+ exec = "cd #{source_path}; zip -ry #{destination_file} *"
119
119
  remote_execute(exec, nil, true)
120
120
  rescue Exception => e
121
121
  raise Exception.new("zip failed due to #{e.message}")
@@ -1,37 +1,39 @@
1
1
  # Contains methods that are used by the scripts in the state-machines. Since
2
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
3
+ #
4
+ # Note: it is supposed that a hash named @context exists @context[:script]
5
+ # must be set to a script object to pass information and messages
6
+ # to listeners.
7
+ # Some other information is expected to be provided in the @context object:
8
+ # * :remote_command_handler => ssh wrapper object
9
+ # * :ec2_api_handler => wrapper object around EC2 API access
7
10
 
8
11
  module StateTransitionHelper
9
12
 
10
13
  # 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
14
+ # Params:
15
+ # * dns_name => machine to connect to
16
+ # * ssh_keyfile => key-file used for ssh
17
+ # * ssh_keydata => contents of key-file (either use ssh_keyfile or ssh_keydata)
18
+ # Returns:
19
+ # * OS of the connected machine
20
+ def connect(dns_name, ssh_keyfile = nil, ssh_keydata = nil)
21
+ post_message("connecting to #{dns_name}...")
20
22
  connected = false
21
23
  remaining_trials = 3
22
24
  while !connected && remaining_trials > 0
23
25
  remaining_trials -= 1
24
- if @context[:ssh_keyfile] != nil
26
+ if ssh_keyfile != nil
25
27
  begin
26
- @context[:remote_command_handler].connect_with_keyfile(@context[:dns_name], @context[:ssh_keyfile])
28
+ remote_handler().connect_with_keyfile(dns_name, ssh_keyfile)
27
29
  connected = true
28
30
  rescue Exception => e
29
31
  @logger.info("connection failed due to #{e}")
30
32
  @logger.debug(e.backtrace.join("\n"))
31
33
  end
32
- elsif @context[:ssh_keydata] != nil
34
+ elsif ssh_keydata != nil
33
35
  begin
34
- @context[:remote_command_handler].connect(@context[:dns_name], "root", @context[:ssh_keydata])
36
+ remote_handler().connect(dns_name, "root", ssh_keydata)
35
37
  connected = true
36
38
  rescue Exception => e
37
39
  @logger.info("connection failed due to #{e}")
@@ -47,82 +49,81 @@ module StateTransitionHelper
47
49
  if !connected
48
50
  raise Exception.new("connection attempts stopped")
49
51
  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]}"
52
+ os = remote_handler().retrieve_os()
53
+ post_message("connected to #{dns_name}. OS installed is #{os}")
54
+ @logger.info "connected to #{dns_name}"
55
+ return os
53
56
  end
54
57
 
55
58
  # 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]}"
59
+ # Input Parameters:
60
+ # * ami_id => ID of the AMI to be launched
61
+ # * key_name => name of the key to access the instance
62
+ # * security_group_name => name of the security group to be used
63
+ # Returned information:
64
+ # * instance_id => ID of the started instance
65
+ # * dns_name => DNS name of the started instance
66
+ # * availability_zone => Availability zone of the started instance
67
+ # * kernel_id => EC2 Kernel ID of the started instance
68
+ # * ramdisk_id => EC2 Ramdisk ID of the started instance
69
+ # * architecture => architecture (e.g. 386i, 64x) of the started instance
70
+ def launch_instance(ami_id, key_name, security_group_name)
71
+ post_message("starting up instance to execute the script (AMI = #{ami_id}) ...")
72
+ @logger.debug "start up AMI #{ami_id}"
71
73
  # find out the image architecture first
72
- image_props = @context[:ec2_api_handler].describe_images(:image_id => @context[:ami_id])
74
+ image_props = ec2_handler().describe_images(:image_id => ami_id)
73
75
  architecture = image_props['imagesSet']['item'][0]['architecture']
74
76
  instance_type = "m1.small"
75
77
  if architecture != "i386"
76
78
  instance_type = "m1.large"
77
79
  end
78
- arch_log_msg = "Architecture of image #{@context[:ami_id]} is #{architecture}. Use instance_type #{instance_type}."
80
+ arch_log_msg = "Architecture of image #{ami_id} is #{architecture}. Use instance_type #{instance_type}."
79
81
  @logger.info arch_log_msg
80
- @context[:script].post_message(arch_log_msg)
82
+ post_message(arch_log_msg)
81
83
  # now start it
82
- res = @context[:ec2_api_handler].run_instances(:image_id => @context[:ami_id],
83
- :security_group => @context[:security_group_name], :key_name => @context[:key_name],
84
+ res = ec2_handler().run_instances(:image_id => ami_id,
85
+ :security_group => security_group_name, :key_name => key_name,
84
86
  :instance_type => instance_type
85
87
  )
86
88
  instance_id = res['instancesSet']['item'][0]['instanceId']
87
- @context[:instance_id] = instance_id
88
89
  @logger.info "started instance #{instance_id}"
89
- @context[:script].post_message("Started instance #{instance_id}. wait until it is ready...")
90
+ post_message("Started instance #{instance_id}. wait until it is ready...")
90
91
  #availability_zone , key_name/group_name
91
92
  started = false
92
93
  while started == false
93
94
  sleep(5)
94
- res = @context[:ec2_api_handler].describe_instances(:instance_id => @context[:instance_id])
95
+ res = ec2_handler().describe_instances(:instance_id => instance_id)
95
96
  state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
96
97
  @logger.info "instance is in state #{state['name']} (#{state['code']})"
97
98
  if state['code'].to_i == 16
98
99
  started = true
99
- @context[:script].post_message("instance is up and running")
100
- @context[:dns_name] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['dnsName']
101
- @context[:availability_zone] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['placement']['availabilityZone']
102
- @context[:kernel_id] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['kernelId']
103
- @context[:ramdisk_id] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['ramdiskId']
104
- @context[:architecture] = res['reservationSet']['item'][0]['instancesSet']['item'][0]['architecture']
100
+ post_message("instance is up and running")
101
+ dns_name = res['reservationSet']['item'][0]['instancesSet']['item'][0]['dnsName']
102
+ availability_zone = res['reservationSet']['item'][0]['instancesSet']['item'][0]['placement']['availabilityZone']
103
+ kernel_id = res['reservationSet']['item'][0]['instancesSet']['item'][0]['kernelId']
104
+ ramdisk_id = res['reservationSet']['item'][0]['instancesSet']['item'][0]['ramdiskId']
105
+ architecture = res['reservationSet']['item'][0]['instancesSet']['item'][0]['architecture']
105
106
  elsif state['code'].to_i != 0
106
- @context[:script].post_message("instance in state #{state['name']}")
107
+ post_message("instance in state #{state['name']}")
107
108
  raise Exception.new('instance failed to start up')
108
109
  else
109
- @context[:script].post_message("instance still starting up...")
110
+ post_message("instance still starting up...")
110
111
  end
111
112
  end
113
+ return instance_id, dns_name, availability_zone, kernel_id, ramdisk_id, architecture
112
114
  end
113
115
 
114
116
  # Shuts down an instance.
115
- # Input Parameters in @context:
116
- # * :instance_id => ID of the instance to be shut down
117
- # * :ec2_api_handler => wrapper object around EC2 API access
118
- def shut_down_instance()
119
- @context[:script].post_message("going to shut down the temporary instance #{@context[:instance_id]}...")
120
- @logger.debug "shutdown instance #{@context[:instance_id]}"
121
- res = @context[:ec2_api_handler].terminate_instances(:instance_id => @context[:instance_id])
117
+ # Input Parameters:
118
+ # * instance_id => ID of the instance to be shut down
119
+ def shut_down_instance(instance_id)
120
+ post_message("going to shut down the temporary instance #{instance_id}...")
121
+ @logger.debug "shutdown instance #{instance_id}"
122
+ res = ec2_handler().terminate_instances(:instance_id => instance_id)
122
123
  done = false
123
124
  while done == false
124
125
  sleep(5)
125
- res = @context[:ec2_api_handler].describe_instances(:instance_id => @context[:instance_id])
126
+ res = ec2_handler().describe_instances(:instance_id => instance_id)
126
127
  state = res['reservationSet']['item'][0]['instancesSet']['item'][0]['instanceState']
127
128
  @logger.debug "instance in state #{state['name']} (#{state['code']})"
128
129
  if state['code'].to_i == 48
@@ -131,245 +132,267 @@ module StateTransitionHelper
131
132
  raise Exception.new('instance failed to shut down')
132
133
  end
133
134
  end
134
- @context[:script].post_message("instance #{@context[:instance_id]} is terminated")
135
+ post_message("instance #{instance_id} is terminated")
135
136
  end
136
137
 
137
138
  # Creates a new EBS volume.
138
- # Input Parameters in @context:
139
- # * :availability_zone => availability zone for the volume
140
- # * :ec2_api_handler => wrapper object around EC2 API access
141
- # Output information set by this method in @context:
142
- # * :volume_id => EC2 EBS Volume ID
143
- def create_volume()
144
- @context[:script].post_message("going to create a new EBS volume...")
145
- @logger.debug "create volume in zone #{@context[:availability_zone]}"
146
- res = @context[:ec2_api_handler].create_volume(:availability_zone => @context[:availability_zone], :size => "10")
147
- @context[:volume_id] = res['volumeId']
139
+ # Input Parameters:
140
+ # * availability_zone => availability zone for the volume
141
+ # * size => size in Gigabytes
142
+ # Returns
143
+ # * volume_id => EC2 EBS Volume ID
144
+ def create_volume(availability_zone, size = "10")
145
+ post_message("going to create a new EBS volume of size #{size}GB...")
146
+ @logger.debug "create volume in zone #{availability_zone}"
147
+ res = ec2_handler().create_volume(:availability_zone => availability_zone, :size => size.to_s)
148
+ volume_id = res['volumeId']
148
149
  started = false
149
150
  while !started
150
151
  sleep(5)
151
152
  #TODO: check for timeout?
152
- res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
153
+ res = ec2_handler().describe_volumes(:volume_id => volume_id)
153
154
  state = res['volumeSet']['item'][0]['status']
154
155
  @logger.debug "volume state #{state}"
155
156
  if state == 'available'
156
157
  started = true
157
158
  end
158
159
  end
159
- @context[:script].post_message("EBS volume #{@context[:volume_id]} is ready")
160
+ post_message("EBS volume #{volume_id} is ready")
161
+ return volume_id
160
162
  end
161
163
 
162
164
  # Creates a new EBS volume from a snapshot ID.
163
- # Input Parameters in @context:
164
- # * :availability_zone => availability zone for the volume
165
- # * :snapshot_id => EC2 Snapshot ID used to create the volume
166
- # * :ec2_api_handler => wrapper object around EC2 API access
167
- # Output information set by this method in @context:
168
- # * :volume_id => EC2 EBS Volume ID created
169
- def create_volume_from_snapshot
170
- @context[:script].post_message("going to create a new EBS volume from the specified snapshot...")
171
- @logger.debug "create volume in zone #{@context[:availability_zone]}"
172
- res = @context[:ec2_api_handler].create_volume(:snapshot_id => @context[:snapshot_id], :availability_zone => @context[:availability_zone], :size => "10")
173
- @context[:volume_id] = res['volumeId']
165
+ # Input Parameters:
166
+ # * availability_zone => availability zone for the volume
167
+ # * size => size of the volume to be created
168
+ # * snapshot_id => EC2 Snapshot ID used to create the volume
169
+ # Returns
170
+ # * volume_id => EC2 EBS Volume ID created
171
+ def create_volume_from_snapshot(snapshot_id, availability_zone)
172
+ post_message("going to create a new EBS volume from the specified snapshot...")
173
+ @logger.debug "create volume in zone #{availability_zone}"
174
+ res = ec2_handler().create_volume(:snapshot_id => snapshot_id, :availability_zone => availability_zone)
175
+ volume_id = res['volumeId']
174
176
  started = false
175
177
  while !started
176
178
  sleep(5)
177
179
  #TODO: check for timeout?
178
- res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
180
+ res = ec2_handler().describe_volumes(:volume_id => volume_id)
179
181
  state = res['volumeSet']['item'][0]['status']
180
182
  @logger.debug "volume state #{state}"
181
183
  if state == 'available'
182
184
  started = true
183
185
  end
184
186
  end
185
- @context[:script].post_message("EBS volume #{@context[:volume_id]} is ready")
187
+ post_message("EBS volume #{volume_id} is ready")
188
+ return volume_id
186
189
  end
187
190
 
188
191
  # Attaches an EBS volume to an instance
189
- # Input Parameters in @context:
190
- # * :volume_id => EC2 ID for the EBS Volume to be attached
191
- # * :instance_id => EC2 ID for the instance to which the volume is supposed to be attached to
192
- # * :temp_device_name => device name to be used for attaching (e.g. /dev/sdj1)
193
- # * :ec2_api_handler => wrapper object around EC2 API access
194
- def attach_volume
195
- @context[:script].post_message("going to attach volume #{@context[:volume_id]} to instance #{@context[:instance_id]} on device #{@context[:temp_device_name]}...")
196
- @logger.debug "attach volume #{@context[:volume_id]} to instance #{@context[:instance_id]} on device #{@context[:temp_device_name]}"
197
- @context[:ec2_api_handler].attach_volume(:volume_id => @context[:volume_id],
198
- :instance_id => @context[:instance_id],
199
- :device => @context[:temp_device_name]
192
+ # Input Parameters:
193
+ # * volume_id => EC2 ID for the EBS Volume to be attached
194
+ # * instance_id => EC2 ID for the instance to which the volume is supposed to be attached to
195
+ # * temp_device_name => device name to be used for attaching (e.g. /dev/sdj1)
196
+ def attach_volume(volume_id, instance_id, temp_device_name)
197
+ post_message("going to attach volume #{volume_id} to instance #{instance_id} on device #{temp_device_name}...")
198
+ @logger.debug "attach volume #{volume_id} to instance #{instance_id} on device #{temp_device_name}"
199
+ ec2_handler().attach_volume(:volume_id => volume_id,
200
+ :instance_id => instance_id,
201
+ :device => temp_device_name
200
202
  )
201
203
  done = false
202
204
  while !done
203
205
  sleep(5)
204
206
  #TODO: check for timeout?
205
- res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
207
+ res = ec2_handler().describe_volumes(:volume_id => volume_id)
206
208
  state = res['volumeSet']['item'][0]['status']
207
209
  @logger.debug "storage attaching: #{state}"
208
210
  if state == 'in-use'
209
211
  done = true
210
212
  end
211
213
  end
212
- @context[:script].post_message("volume successfully attached")
214
+ post_message("volume successfully attached")
213
215
  end
214
216
 
215
217
  # Detach an EBS volume from an instance.
216
- # Input Parameters in @context:
217
- # * :volume_id => EC2 ID for the EBS Volume to be detached
218
- # * :instance_id => EC2 ID for the instance to detach from
219
- # * :ec2_api_handler => wrapper object around EC2 API access
220
- def detach_volume()
221
- @context[:script].post_message("going to detach volume #{@context[:volume_id]}...")
222
- @logger.debug "detach volume #{@context[:volume_id]}"
223
- @context[:ec2_api_handler].detach_volume(:volume_id => @context[:volume_id],
224
- :instance_id => @context[:instance_id]
218
+ # Input Parameters:
219
+ # * volume_id => EC2 ID for the EBS Volume to be detached
220
+ # * instance_id => EC2 ID for the instance to detach from
221
+ def detach_volume(volume_id, instance_id)
222
+ post_message("going to detach volume #{volume_id}...")
223
+ @logger.debug "detach volume #{volume_id}"
224
+ ec2_handler().detach_volume(:volume_id => volume_id,
225
+ :instance_id => instance_id
225
226
  )
226
227
  done = false
227
228
  while !done
228
229
  sleep(3)
229
230
  #TODO: check for timeout?
230
- res = @context[:ec2_api_handler].describe_volumes(:volume_id => @context[:volume_id])
231
+ res = ec2_handler().describe_volumes(:volume_id => volume_id)
231
232
  @logger.debug "volume detaching: #{res.inspect}"
232
233
  if res['volumeSet']['item'][0]['status'] == 'available'
233
234
  done = true
234
235
  end
235
236
  end
236
- @context[:script].post_message("volume #{@context[:volume_id]} detached.")
237
+ post_message("volume #{volume_id} detached.")
237
238
  end
238
239
 
239
240
  # Delete an EBS volume.
240
- # Input Parameters in @context:
241
- # * :volume_id => EC2 ID for the EBS Volume to be deleted
242
- # * :ec2_api_handler => wrapper object around EC2 API access
243
- def delete_volume
244
- @context[:script].post_message("going to delete volume #{@context[:volume_id]} (no longer needed)...")
245
- @logger.debug "delete volume #{@context[:volume_id]}"
246
- @context[:ec2_api_handler].delete_volume(:volume_id => @context[:volume_id])
247
- @context[:script].post_message("volume #{@context[:volume_id]} deleted")
241
+ # Input Parameters:
242
+ # * volume_id => EC2 ID for the EBS Volume to be deleted
243
+ def delete_volume(volume_id)
244
+ post_message("going to delete volume #{volume_id} (no longer needed)...")
245
+ @logger.debug "delete volume #{volume_id}"
246
+ ec2_handler().delete_volume(:volume_id => volume_id)
247
+ post_message("volume #{volume_id} deleted")
248
248
  end
249
249
 
250
250
  # Creates a snapshot for an EBS volume.
251
- # Input Parameters in @context:
252
- # * :volume_id => EC2 ID for the EBS volume to be snapshotted
253
- # * :snapshot_id => EC2 Snapshot ID used to create the volume
254
- # * :ec2_api_handler => wrapper object around EC2 API access
255
- # Output information set by this method in @context:
256
- # * :snapshot_id => EC2 ID for the snapshot created
257
- def create_snapshot()
258
- @context[:script].post_message("going to create a snapshot...")
259
- @logger.debug "create snapshot for volume #{@context[:volume_id]}"
260
- res = @context[:ec2_api_handler].create_snapshot(:volume_id => @context[:volume_id])
261
- @context[:snapshot_id] = res['snapshotId']
262
- @logger.info "snapshot_id = #{@context[:snapshot_id]}"
251
+ # Input Parameters::
252
+ # * volume_id => EC2 ID for the EBS volume to be snapshotted
253
+ # Returns:
254
+ # * snapshot_id => EC2 ID for the snapshot created
255
+ def create_snapshot(volume_id)
256
+ post_message("going to create a snapshot for volume #{volume_id}...")
257
+ @logger.debug "create snapshot for volume #{volume_id}"
258
+ res = ec2_handler().create_snapshot(:volume_id => volume_id)
259
+ snapshot_id = res['snapshotId']
260
+ @logger.info "snapshot_id = #{snapshot_id}"
263
261
  done = false
264
262
  while !done
265
263
  sleep(5)
266
264
  #TODO: check for timeout?
267
- res = @context[:ec2_api_handler].describe_snapshots(:snapshot_id => @context[:snapshot_id])
265
+ res = ec2_handler().describe_snapshots(:snapshot_id => snapshot_id)
268
266
  @logger.debug "snapshot creating: #{res.inspect}"
269
267
  if res['snapshotSet']['item'][0]['status'] == 'completed'
270
268
  done = true
271
269
  end
272
270
  end
273
- @context[:script].post_message("snapshot is done with ID=#{@context[:snapshot_id]}")
271
+ post_message("snapshot is done with ID=#{snapshot_id}")
272
+ return snapshot_id
274
273
  end
275
274
 
276
275
  # Registers a snapshot as EBS-booted AMI.
277
- # Input Parameters in @context:
278
- # * :snapshot_id => EC2 Snapshot ID used to be used
279
- # * :name => name of the AMI to be created
280
- # * :root_device_name => Root device name (e.g. /dev/sdj) to be used for AMI registration
281
- # * :description => description of the AMI to be created
282
- # * :kernel_id => EC2 Kernel ID to be used for AMI registration
283
- # * :ramdisk_id => EC2 Ramdisk ID to be used for AMI registration
284
- # * :architecture => architecture (e.g. 386i, 64x) to be used for AMI registration
285
- # * :ec2_api_handler => wrapper object around EC2 API access
286
- # Output information set by this method in @context:
287
- # * {:result => :image_id} => ID of the AMI created and registered
288
- def register_snapshot()
289
- @context[:script].post_message("going to register snapshot #{@context[:snapshot_id]}...")
290
- @logger.debug "register snapshot #{@context[:snapshot_id]} as #{@context[:name]}"
291
- res = @context[:ec2_api_handler].register_image_updated(:snapshot_id => @context[:snapshot_id],
292
- :kernel_id => @context[:kernel_id], :architecture => @context[:architecture],
293
- :root_device_name => @context[:root_device_name],
294
- :description => @context[:description], :name => @context[:name],
295
- :ramdisk_id => @context[:ramdisk_id]
276
+ # Input Parameters:
277
+ # * snapshot_id => EC2 Snapshot ID used to be used
278
+ # * name => name of the AMI to be created
279
+ # * root_device_name => Root device name (e.g. /dev/sdj) to be used for AMI registration
280
+ # * description => description of the AMI to be created
281
+ # * kernel_id => EC2 Kernel ID to be used for AMI registration
282
+ # * ramdisk_id => EC2 Ramdisk ID to be used for AMI registration
283
+ # * architecture => architecture (e.g. 386i, 64x) to be used for AMI registration
284
+ # Returns:
285
+ # * image_id => ID of the AMI created and registered
286
+ def register_snapshot(snapshot_id, name, root_device_name, description, kernel_id, ramdisk_id, architecture)
287
+ post_message("going to register snapshot #{snapshot_id}...")
288
+ @logger.debug "register snapshot #{snapshot_id} as #{name}"
289
+ res = ec2_handler().register_image_updated(:snapshot_id => snapshot_id,
290
+ :kernel_id => kernel_id, :architecture => architecture,
291
+ :root_device_name => root_device_name,
292
+ :description => description, :name => name,
293
+ :ramdisk_id => ramdisk_id
296
294
  )
297
295
  @logger.debug "result of registration = #{res.inspect}"
298
- @context[:result][:image_id] = res['imageId']
299
- @logger.info "resulting image_id = #{@context[:result][:image_id]}"
300
- @context[:script].post_message("snapshot #{@context[:snapshot_id]} successfully registered as AMI #{@context[:result][:image_id]} ")
296
+ image_id = res['imageId']
297
+ @logger.info "resulting image_id = #{image_id}"
298
+ post_message("snapshot #{snapshot_id} successfully registered as AMI #{image_id} ")
299
+ return image_id
301
300
  end
302
301
 
303
302
  # Create a file-system on a given machine (assumes to be connected already).
304
- # Input Parameters in @context:
305
- # * :dns_name => IP used
306
- # * :temp_device_name => device name to be used #TODO: give it a better name
307
- # * :remote_command_handler => ssh wrapper object
308
- def create_fs()
309
- @context[:script].post_message("going to create filesystem on #{@context[:dns_name]} to #{@context[:temp_device_name]}...")
310
- @logger.debug "create filesystem on #{@context[:dns_name]} to #{@context[:temp_device_name]}"
311
- @context[:remote_command_handler].create_filesystem("ext3", @context[:temp_device_name])
312
- @context[:script].post_message("filesystem system successfully created")
303
+ # Input Parameters:
304
+ # * dns_name => IP used
305
+ # * device => device to be used for file-system creation (e.g. /dev/sdj)
306
+ def create_fs(dns_name, device)
307
+ post_message("going to create filesystem on #{dns_name} to #{device}...")
308
+ @logger.debug "create filesystem on #{dns_name} to #{device}"
309
+ remote_handler().create_filesystem("ext3", device)
310
+ post_message("filesystem system successfully created")
313
311
  end
314
312
 
315
313
  # Create a file-system on a given machine (assumes to be connected already).
316
- # Input Parameters in @context:
317
- # * :path => mount point #TODO: give it a better name
318
- # * :temp_device_name => device used for mounting #TODO: give it a better name
319
- # * :remote_command_handler => ssh wrapper object
320
- def mount_fs()
321
- @context[:path] = "/mnt/tmp_#{@context[:volume_id]}"
322
- @context[:script].post_message("going to mount #{@context[:temp_device_name]} on #{@context[:path]}...")
323
- @logger.debug "mount #{@context[:temp_device_name]} on #{@context[:path]}"
324
- @context[:remote_command_handler].mkdir(@context[:path])
325
- @context[:remote_command_handler].mount(@context[:temp_device_name], @context[:path])
326
- sleep(2) #give mount some time
327
- if !@context[:remote_command_handler].drive_mounted?(@context[:path])
328
- raise Exception.new("drive #{@context[:path]} not mounted")
314
+ # Input Parameters:
315
+ # * mount_point => directory to be mounted on the device
316
+ # * device => device used for mounting
317
+ def mount_fs(mount_point, device)
318
+ post_message("going to mount #{device} on #{mount_point}...")
319
+ @logger.debug "mount #{device} on #{mount_point}"
320
+ if !remote_handler.file_exists?(mount_point)
321
+ remote_handler().mkdir(mount_point)
322
+ end
323
+ remote_handler().mount(device, mount_point)
324
+ trials = 3
325
+ mounted = false
326
+ while trials > 0
327
+ sleep(5) #give mount some time
328
+ if remote_handler().drive_mounted?(mount_point)
329
+ mounted = true
330
+ break
331
+ end
332
+ trials -= trials
329
333
  end
330
- @context[:script].post_message("mount successful")
334
+ if !mounted
335
+ raise Exception.new("drive #{mount_point} not mounted")
336
+ end
337
+ post_message("mount successful")
331
338
  end
332
339
 
333
340
  # Unmount a drive
334
- # Input Parameters in @context:
335
- # * :path => mount point #TODO: give it a better name
336
- # * :remote_command_handler => ssh wrapper object
337
- def unmount_fs()
338
- @context[:script].post_message("Going to unmount ...")
339
- @logger.debug "unmount #{@context[:path]}"
340
- @context[:remote_command_handler].umount(@context[:path])
341
+ # Input Parameters:
342
+ # * mount_point => directory to be unmounted
343
+ def unmount_fs(mount_point)
344
+ post_message("Going to unmount ...")
345
+ @logger.debug "unmount #{mount_point}"
346
+ remote_handler().umount(mount_point)
341
347
  sleep(2) #give umount some time
342
- if @context[:remote_command_handler].drive_mounted?(@context[:path])
343
- raise Exception.new("drive #{@context[:path]} not unmounted")
348
+ if remote_handler().drive_mounted?(mount_point)
349
+ raise Exception.new("drive #{mount_point} not unmounted")
344
350
  end
345
- @context[:script].post_message("device unmounted")
351
+ post_message("device unmounted")
346
352
  end
347
353
 
348
354
  # Copy all files of a running linux distribution via rsync to a mounted directory
349
- # Input Parameters in @context:
350
- # * :path => where to copy to
351
- # * :remote_command_handler => ssh wrapper object
352
- def copy()
353
- @context[:script].post_message("going to start copying files to #{@context[:path]}. This may take quite a time...")
354
- @logger.debug "start copying to #{@context[:path]}"
355
+ # Input Parameters:
356
+ # * destination_path => where to copy to
357
+ def copy_distribution(destination_path)
358
+ post_message("going to start copying files to #{destination_path}. This may take quite a time...")
359
+ @logger.debug "start copying to #{destination_path}"
355
360
  start = Time.new.to_i
356
- @context[:remote_command_handler].rsync("/", "#{@context[:path]}", "#{@context[:path]}")
357
- @context[:remote_command_handler].rsync("/dev/", "#{@context[:path]}/dev/")
361
+ remote_handler().rsync("/", "#{destination_path}", "#{destination_path}")
362
+ remote_handler().rsync("/dev/", "#{destination_path}/dev/")
358
363
  endtime = Time.new.to_i
359
364
  @logger.info "copy took #{(endtime-start)}s"
360
- @context[:script].post_message("copying is done (took #{endtime-start})s")
365
+ post_message("copying is done (took #{endtime-start})s")
361
366
  end
362
367
 
363
368
  # Zips all files on a mounted-directory into a file
364
- # Input Parameters in @context:
365
- # * :path => where to copy from
366
- # * :zip_file_dest => path where the zip-file should be stored
367
- # # :zip_file_name => name of the zip file (without .zip suffix)
368
- # * :remote_command_handler => ssh wrapper object
369
- def zip_volume
370
- @context[:script].post_message("going to zip the EBS volume")
371
- @context[:remote_command_handler].zip(@context[:path], @context[:zip_file_dest]+@context[:zip_file_name])
372
- @context[:script].post_message("EBS volume successfully zipped")
369
+ # Input Parameters:
370
+ # * source_dir => where to copy from
371
+ # * zip_file_dest => path where the zip-file should be stored
372
+ # # zip_file_name => name of the zip file (without .zip suffix)
373
+ def zip_volume(source_dir, zip_file_dest, zip_file_name)
374
+ post_message("going to zip the EBS volume")
375
+ remote_handler().zip(source_dir, zip_file_dest+"/"+zip_file_name)
376
+ post_message("EBS volume successfully zipped")
377
+ end
378
+
379
+ protected
380
+
381
+ def post_message(msg)
382
+ if @context[:script] != nil
383
+ @context[:script].post_message(msg)
384
+ end
385
+ end
386
+
387
+ def remote_handler()
388
+ if @context[:remote_command_handler] == nil
389
+ @context[:remote_command_handler] = RemoteCommandHandler.new
390
+ end
391
+ @context[:remote_command_handler]
392
+ end
393
+
394
+ def ec2_handler()
395
+ @context[:ec2_api_handler]
373
396
  end
374
397
 
375
398
  end
@@ -76,7 +76,9 @@ class Ami2EbsConversion < Ec2Script
76
76
  # which serves to create
77
77
  class InitialState < Ami2EbsConversionState
78
78
  def enter
79
- launch_instance()
79
+ @context[:instance_id], @context[:dns_name], @context[:availability_zone],
80
+ @context[:kernel_id], @context[:ramdisk_id], @context[:architecture] =
81
+ launch_instance(@context[:ami_id], @context[:key_name], @context[:security_group_name])
80
82
  AmiStarted.new(@context)
81
83
  end
82
84
  end
@@ -84,7 +86,7 @@ class Ami2EbsConversion < Ec2Script
84
86
  # Ami started. Create a storage
85
87
  class AmiStarted < Ami2EbsConversionState
86
88
  def enter
87
- create_volume()
89
+ @context[:volume_id] = create_volume(@context[:availability_zone], "10")
88
90
  StorageCreated.new(@context)
89
91
  end
90
92
  end
@@ -92,7 +94,7 @@ class Ami2EbsConversion < Ec2Script
92
94
  # Storage created. Attach it.
93
95
  class StorageCreated < Ami2EbsConversionState
94
96
  def enter
95
- attach_volume()
97
+ attach_volume(@context[:volume_id], @context[:instance_id], @context[:temp_device_name])
96
98
  StorageAttached.new(@context)
97
99
  end
98
100
  end
@@ -100,8 +102,9 @@ class Ami2EbsConversion < Ec2Script
100
102
  # Storage attached. Create a file-system and moun it
101
103
  class StorageAttached < Ami2EbsConversionState
102
104
  def enter
103
- connect()
104
- create_fs()
105
+ @context[:result][:os] =
106
+ connect(@context[:dns_name], @context[:ssh_keyfile], @context[:ssh_keydata])
107
+ create_fs(@context[:dns_name], @context[:temp_device_name])
105
108
  FileSystemCreated.new(@context)
106
109
  end
107
110
  end
@@ -109,7 +112,8 @@ class Ami2EbsConversion < Ec2Script
109
112
  # File system created. Mount it.
110
113
  class FileSystemCreated < Ami2EbsConversionState
111
114
  def enter
112
- mount_fs()
115
+ @context[:mount_dir] = "/mnt/tmp_#{@context[:volume_id]}"
116
+ mount_fs(@context[:mount_dir], @context[:temp_device_name])
113
117
  FileSystemMounted.new(@context)
114
118
  end
115
119
  end
@@ -117,7 +121,7 @@ class Ami2EbsConversion < Ec2Script
117
121
  # File system created and mounted. Copy the root partition.
118
122
  class FileSystemMounted < Ami2EbsConversionState
119
123
  def enter
120
- copy()
124
+ copy_distribution(@context[:mount_dir])
121
125
  CopyDone.new(@context)
122
126
  end
123
127
  end
@@ -125,7 +129,7 @@ class Ami2EbsConversion < Ec2Script
125
129
  # Copy operation done. Unmount volume.
126
130
  class CopyDone < Ami2EbsConversionState
127
131
  def enter
128
- unmount_fs()
132
+ unmount_fs(@context[:mount_dir])
129
133
  VolumeUnmounted.new(@context)
130
134
  end
131
135
  end
@@ -133,7 +137,7 @@ class Ami2EbsConversion < Ec2Script
133
137
  # Volume unmounted. Detach it.
134
138
  class VolumeUnmounted < Ami2EbsConversionState
135
139
  def enter
136
- detach_volume()
140
+ detach_volume(@context[:volume_id], @context[:instance_id])
137
141
  VolumeDetached.new(@context)
138
142
  end
139
143
  end
@@ -141,7 +145,7 @@ class Ami2EbsConversion < Ec2Script
141
145
  # VolumeDetached. Create snaphot
142
146
  class VolumeDetached < Ami2EbsConversionState
143
147
  def enter
144
- create_snapshot()
148
+ @context[:snapshot_id] = create_snapshot(@context[:volume_id])
145
149
  SnapshotCreated.new(@context)
146
150
  end
147
151
  end
@@ -149,7 +153,7 @@ class Ami2EbsConversion < Ec2Script
149
153
  # Snapshot created. Delete volume.
150
154
  class SnapshotCreated < Ami2EbsConversionState
151
155
  def enter
152
- delete_volume()
156
+ delete_volume(@context[:volume_id])
153
157
  VolumeDeleted.new(@context)
154
158
  end
155
159
  end
@@ -157,7 +161,9 @@ class Ami2EbsConversion < Ec2Script
157
161
  # Volume deleted. Register snapshot.
158
162
  class VolumeDeleted < Ami2EbsConversionState
159
163
  def enter
160
- register_snapshot()
164
+ @context[:result][:image_id] = register_snapshot(@context[:snapshot_id], @context[:name],
165
+ @context[:root_device_name], @context[:description], @context[:kernel_id],
166
+ @context[:ramdisk_id], @context[:architecture])
161
167
  SnapshotRegistered.new(@context)
162
168
  end
163
169
  end
@@ -165,7 +171,7 @@ class Ami2EbsConversion < Ec2Script
165
171
  # Snapshot registered. Shutdown instance.
166
172
  class SnapshotRegistered < Ami2EbsConversionState
167
173
  def enter
168
- shut_down_instance()
174
+ shut_down_instance(@context[:instance_id])
169
175
  Done.new(@context)
170
176
  end
171
177
  end
@@ -61,7 +61,8 @@ class DmEncrypt < Ec2Script
61
61
  # Starting state. Tries to connect via ssh.
62
62
  class InitialState < DmEncryptState
63
63
  def enter
64
- connect()
64
+ @context[:result][:os] =
65
+ connect(@context[:dns_name], @context[:ssh_keyfile], @context[:ssh_keydata])
65
66
  install_tools()
66
67
  end
67
68
 
@@ -2,6 +2,7 @@ require "help/script_execution_state"
2
2
  require "scripts/ec2/ec2_script"
3
3
  require "help/remote_command_handler"
4
4
  require "help/dm_crypt_helper"
5
+ require "help/ec2_helper"
5
6
  require "AWS"
6
7
 
7
8
  # Script to download a specific snapshot as ZIP
@@ -31,11 +32,14 @@ class DownloadSnapshot < Ec2Script
31
32
  end
32
33
 
33
34
  def check_input_parameters()
34
- if @input_params[:temp_device_name] == nil
35
- @input_params[:temp_device_name] = "/dev/sdj"
35
+ if @input_params[:source_device] == nil
36
+ @input_params[:source_device] = "/dev/sdj1"
37
+ end
38
+ if @input_params[:dest_device] == nil
39
+ @input_params[:dest_device] = "/dev/sdj2"
36
40
  end
37
41
  if @input_params[:zip_file_dest] == nil
38
- @input_params[:zip_file_dest] = "/var/www/html/"
42
+ @input_params[:zip_file_dest] = "/var/www/html"
39
43
  end
40
44
  if @input_params[:zip_file_name] == nil
41
45
  @input_params[:zip_file_name] = "download"
@@ -61,11 +65,13 @@ class DownloadSnapshot < Ec2Script
61
65
  end
62
66
  end
63
67
 
64
- #Connected.
65
68
  # Start state. First thing to do is to launch the instance.
66
69
  class InitialState < DownloadSnapshotState
67
70
  def enter
68
- launch_instance()
71
+ result = launch_instance(@context[:ami_id], @context[:key_name], @context[:security_group_name])
72
+ @context[:instance_id] = result.first
73
+ @context[:dns_name] = result[1]
74
+ @context[:availability_zone] = result[2]
69
75
  InstanceLaunchedState.new(context)
70
76
  end
71
77
  end
@@ -73,42 +79,46 @@ class DownloadSnapshot < Ec2Script
73
79
  # Instance Launched. Create a volume based on the snapshot.
74
80
  class InstanceLaunchedState < DownloadSnapshotState
75
81
  def enter
76
- create_volume_from_snapshot()
77
- VolumeCreated.new(@context)
82
+ @context[:source_volume_id] = create_volume_from_snapshot(@context[:snapshot_id], @context[:availability_zone])
83
+ ec2_helper = Ec2Helper.new(@context[:ec2_api_handler])
84
+ size = ec2_helper.volume_prop(@context[:source_volume_id], :size).to_i
85
+ puts "retrieved volume size of #{size}"
86
+ @context[:dest_volume_id] = create_volume(@context[:availability_zone], size)
87
+ VolumesCreated.new(@context)
78
88
  end
79
89
 
80
90
  end
81
91
 
82
- # Volume created. Attach it.
83
- class VolumeCreated < DownloadSnapshotState
84
- def enter
85
- attach_volume()
86
- VolumeAttached.new(@context)
87
- end
88
- end
89
-
90
- # Volume attached. Create a file-system and mount it.
91
- class VolumeAttached < DownloadSnapshotState
92
+ # Volumes created. Attach it.
93
+ class VolumesCreated < DownloadSnapshotState
92
94
  def enter
93
- connect()
94
- create_fs()
95
- FileSystemCreated.new(@context)
95
+ @context[:script].post_message("Going to create two volumes. One with the snapshot data, one to store the zipped data for download.")
96
+ attach_volume(@context[:source_volume_id], @context[:instance_id], @context[:source_device])
97
+ attach_volume(@context[:dest_volume_id], @context[:instance_id], @context[:dest_device])
98
+ VolumesAttached.new(@context)
96
99
  end
97
100
  end
98
101
 
99
- # File system created. Mount it.
100
- class FileSystemCreated < DownloadSnapshotState
102
+ # Volumes attached. Create a file-system for the destination one, and mount both.
103
+ class VolumesAttached < DownloadSnapshotState
101
104
  def enter
102
- mount_fs()
103
- FileSystemMounted.new(@context)
105
+ @context[:script].post_message("Going to prepare the two volumes for the zip-operation.")
106
+ @context[:result][:os] =
107
+ connect(@context[:dns_name], @context[:ssh_keyfile], @context[:ssh_keydata])
108
+ source_dir = "/mnt/tmp_#{@context[:source_volume_id]}"
109
+ dest_dir = @context[:zip_file_dest]
110
+ create_fs(@context[:dns_name], @context[:dest_device])
111
+ mount_fs(source_dir, @context[:source_device])
112
+ mount_fs(dest_dir, @context[:dest_device])
113
+ FileSystemsReady.new(@context)
104
114
  end
105
-
106
115
  end
107
116
 
108
117
  # File System mounted. Zip the complete directory on the EBS.
109
- class FileSystemMounted < DownloadSnapshotState
118
+ class FileSystemsReady < DownloadSnapshotState
110
119
  def enter
111
- zip_volume()
120
+ mount_point = "/mnt/tmp_#{@context[:source_volume_id]}"
121
+ zip_volume(mount_point, @context[:zip_file_dest], @context[:zip_file_name])
112
122
  VolumeZippedAndDownloadableState.new(@context)
113
123
  end
114
124
 
@@ -136,7 +146,7 @@ class DownloadSnapshot < Ec2Script
136
146
  # Snapshot can no longer be downloaded. Shut down the instance.
137
147
  class DownloadStoppedState < DownloadSnapshotState
138
148
  def enter
139
- shut_down_instance()
149
+ shut_down_instance(@context[:instance_id])
140
150
  InstanceShutDown.new(@context)
141
151
  end
142
152
 
@@ -145,7 +155,8 @@ class DownloadSnapshot < Ec2Script
145
155
  # Instance is shut down. Delete the volume created.
146
156
  class InstanceShutDown < DownloadSnapshotState
147
157
  def enter
148
- delete_volume()
158
+ delete_volume(@context[:source_volume_id])
159
+ delete_volume(@context[:dest_volume_id])
149
160
  Done.new(@context)
150
161
  end
151
162
  end
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: CloudyScripts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 14
9
+ version: 0.0.14
5
10
  platform: ruby
6
11
  authors:
7
12
  - Matthias Jung
@@ -9,29 +14,33 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-03-17 00:00:00 +01:00
17
+ date: 2010-03-25 00:00:00 +01:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: amazon-ec2
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
23
29
  version: "0"
24
- version:
30
+ type: :runtime
31
+ version_requirements: *id001
25
32
  - !ruby/object:Gem::Dependency
26
33
  name: net-ssh
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - ">="
32
38
  - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
33
41
  version: "0"
34
- version:
42
+ type: :runtime
43
+ version_requirements: *id002
35
44
  description: Scripts to facilitate programming for infrastructure clouds.
36
45
  email: matthias.jung@gmail.com
37
46
  executables: []
@@ -70,18 +79,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
70
79
  requirements:
71
80
  - - ">="
72
81
  - !ruby/object:Gem::Version
82
+ segments:
83
+ - 0
73
84
  version: "0"
74
- version:
75
85
  required_rubygems_version: !ruby/object:Gem::Requirement
76
86
  requirements:
77
87
  - - ">="
78
88
  - !ruby/object:Gem::Version
89
+ segments:
90
+ - 0
79
91
  version: "0"
80
- version:
81
92
  requirements: []
82
93
 
83
94
  rubyforge_project: cloudyscripts
84
- rubygems_version: 1.3.5
95
+ rubygems_version: 1.3.6
85
96
  signing_key:
86
97
  specification_version: 3
87
98
  summary: Scripts to facilitate programming for infrastructure clouds.