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,206 +1,148 @@
|
|
1
|
-
require "help/script_execution_state"
|
2
|
-
require "scripts/ec2/ec2_script"
|
3
|
-
require "help/remote_command_handler"
|
4
|
-
require "help/dm_crypt_helper"
|
5
|
-
require "AWS"
|
6
|
-
|
7
|
-
# Script to Encrypt an EC2 Storage (aka Elastic Block Storage)
|
8
|
-
#
|
9
|
-
class DmEncrypt < Ec2Script
|
10
|
-
# dependencies: tools that need to be installed to make things work
|
11
|
-
TOOLS = ["cryptsetup"]
|
12
|
-
# the parameters for which
|
13
|
-
CHECK = ["cryptsetup"]
|
14
|
-
|
15
|
-
# Input parameters
|
16
|
-
# * aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
|
17
|
-
# * aws_secret_key => the Amazon AWS Secret Key
|
18
|
-
# * ip_address => IP Address of the machine to connect to
|
19
|
-
# * ssh_key_file => Path of the keyfile used to connect to the machine (optional, otherwise: ssh_key_data)
|
20
|
-
# * ssh_key_data => Key information (optional, otherwise: ssh_key_file)
|
21
|
-
# * device => Path of the device to encrypt
|
22
|
-
# * device_name => Name of the Device to encrypt
|
23
|
-
# * storage_path => Path on which the encrypted device is mounted
|
24
|
-
# * paraphrase => paraphrase used for encryption
|
25
|
-
# * remote_command_handler => object that allows to connect via ssh and execute commands (optional)
|
26
|
-
# * ec2_api_handler => object that allows to access the EC2 API (optional)
|
27
|
-
# * ec2_api_server => server to connect to (option, default is us-east-1.ec2.amazonaws.com)
|
28
|
-
#
|
29
|
-
|
30
|
-
def initialize(input_params)
|
31
|
-
super(input_params)
|
32
|
-
end
|
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
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
@context
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
def tools_installed?
|
150
|
-
CHECK.each() {|tool|
|
151
|
-
if !@context[:remote_command_handler].tools_installed?(tool)
|
152
|
-
return false
|
153
|
-
end
|
154
|
-
}
|
155
|
-
true
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Connected and Tools installed. Start encryption.
|
160
|
-
class ToolInstalledState < DmEncryptState
|
161
|
-
def enter
|
162
|
-
create_encrypted_volume()
|
163
|
-
end
|
164
|
-
|
165
|
-
private
|
166
|
-
def create_encrypted_volume
|
167
|
-
@context[:script].post_message("going to encrypt device #{@context[:device]} "+
|
168
|
-
"named '#{@context[:device_name]}' and mount it as #{@context[:storage_path]}...")
|
169
|
-
@logger.debug "ToolInstalledState.create_encrypted_volume"
|
170
|
-
@context[:remote_command_handler].encrypt_storage(@context[:device_name],
|
171
|
-
@context[:paraphrase], @context[:device], @context[:storage_path])
|
172
|
-
@context[:script].post_message("device #{@context[:device]} is encrypted and mounted")
|
173
|
-
MountedAndActivatedState.new(@context)
|
174
|
-
end
|
175
|
-
|
176
|
-
def calc_device_name
|
177
|
-
dev = @context[:device_name].gsub(/[-]/,"--")
|
178
|
-
"/dev/mapper/vg--#{dev}-lv--#{dev}"
|
179
|
-
end
|
180
|
-
|
181
|
-
end
|
182
|
-
|
183
|
-
# The encrypted storages is mounted and activated. Cleanup and done.
|
184
|
-
class MountedAndActivatedState < DmEncryptState
|
185
|
-
def enter
|
186
|
-
cleanup()
|
187
|
-
end
|
188
|
-
|
189
|
-
private
|
190
|
-
def cleanup()
|
191
|
-
@context[:script].post_message("disconnecting...")
|
192
|
-
@logger.debug "MountedAndActivatedState.cleanup"
|
193
|
-
@context[:remote_command_handler].disconnect()
|
194
|
-
@context[:script].post_message("done")
|
195
|
-
DoneState.new(@context)
|
196
|
-
end
|
197
|
-
|
198
|
-
end
|
199
|
-
|
200
|
-
class DoneState < DmEncryptState
|
201
|
-
def done?
|
202
|
-
true
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
1
|
+
require "help/script_execution_state"
|
2
|
+
require "scripts/ec2/ec2_script"
|
3
|
+
require "help/remote_command_handler"
|
4
|
+
require "help/dm_crypt_helper"
|
5
|
+
require "AWS"
|
6
|
+
|
7
|
+
# Script to Encrypt an EC2 Storage (aka Elastic Block Storage)
|
8
|
+
#
|
9
|
+
class DmEncrypt < Ec2Script
|
10
|
+
# dependencies: tools that need to be installed to make things work
|
11
|
+
TOOLS = ["cryptsetup"]
|
12
|
+
# the parameters for which
|
13
|
+
CHECK = ["cryptsetup"]
|
14
|
+
|
15
|
+
# Input parameters
|
16
|
+
# * aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
|
17
|
+
# * aws_secret_key => the Amazon AWS Secret Key
|
18
|
+
# * ip_address => IP Address of the machine to connect to
|
19
|
+
# * ssh_key_file => Path of the keyfile used to connect to the machine (optional, otherwise: ssh_key_data)
|
20
|
+
# * ssh_key_data => Key information (optional, otherwise: ssh_key_file)
|
21
|
+
# * device => Path of the device to encrypt
|
22
|
+
# * device_name => Name of the Device to encrypt
|
23
|
+
# * storage_path => Path on which the encrypted device is mounted
|
24
|
+
# * paraphrase => paraphrase used for encryption
|
25
|
+
# * remote_command_handler => object that allows to connect via ssh and execute commands (optional)
|
26
|
+
# * ec2_api_handler => object that allows to access the EC2 API (optional)
|
27
|
+
# * ec2_api_server => server to connect to (option, default is us-east-1.ec2.amazonaws.com)
|
28
|
+
#
|
29
|
+
|
30
|
+
def initialize(input_params)
|
31
|
+
super(input_params)
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_input_parameters()
|
35
|
+
if @input_params[:ec2_api_server] == nil
|
36
|
+
@input_params[:ec2_api_server] = "us-east-1.ec2.amazonaws.com"
|
37
|
+
end
|
38
|
+
if @input_params[:remote_command_handler] == nil
|
39
|
+
@input_params[:remote_command_handler] = RemoteCommandHandler.new
|
40
|
+
end
|
41
|
+
if @input_params[:ec2_api_handler] == nil
|
42
|
+
@input_params[:ec2_api_handler] = AWS::EC2::Base.new(:access_key_id => @input_params[:aws_access_key],
|
43
|
+
:secret_access_key => @input_params[:aws_secret_key], :server => @input_params[:ec2_api_server])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_initial_state()
|
48
|
+
DmEncryptState.load_state(@input_params)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Here begins the state machine implementation
|
54
|
+
class DmEncryptState < ScriptExecutionState
|
55
|
+
|
56
|
+
def self.load_state(context)
|
57
|
+
InitialState.new(context)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Starting state. Tries to connect via ssh.
|
62
|
+
class InitialState < DmEncryptState
|
63
|
+
def enter
|
64
|
+
connect()
|
65
|
+
install_tools()
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def install_tools
|
71
|
+
@context[:script].post_message("check if the system has the cryptset-package installed")
|
72
|
+
@logger.debug "ConnectedState.install_tools"
|
73
|
+
if !tools_installed?
|
74
|
+
@context[:script].post_message("cryptset-package not installed. Going to install it...")
|
75
|
+
TOOLS.each() {|tool|
|
76
|
+
@context[:remote_command_handler].install(tool)
|
77
|
+
}
|
78
|
+
end
|
79
|
+
if !@context[:remote_command_handler].remote_execute("modprobe dm_crypt")
|
80
|
+
raise Exception.new("dm-crypt module missing")
|
81
|
+
end
|
82
|
+
if tools_installed?
|
83
|
+
@context[:script].post_message("cryptset-package is available")
|
84
|
+
@logger.debug "system says that tools are installed"
|
85
|
+
ToolInstalledState.new(@context)
|
86
|
+
else
|
87
|
+
FailedState.new(@context, "Installation of Tools failed", ConnectedState.new(@context))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def tools_installed?
|
92
|
+
CHECK.each() {|tool|
|
93
|
+
if !@context[:remote_command_handler].tools_installed?(tool)
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
}
|
97
|
+
true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Connected and Tools installed. Start encryption.
|
102
|
+
class ToolInstalledState < DmEncryptState
|
103
|
+
def enter
|
104
|
+
create_encrypted_volume()
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
def create_encrypted_volume
|
109
|
+
@context[:script].post_message("going to encrypt device #{@context[:device]} "+
|
110
|
+
"named '#{@context[:device_name]}' and mount it as #{@context[:storage_path]}...")
|
111
|
+
@logger.debug "ToolInstalledState.create_encrypted_volume"
|
112
|
+
@context[:remote_command_handler].encrypt_storage(@context[:device_name],
|
113
|
+
@context[:paraphrase], @context[:device], @context[:storage_path])
|
114
|
+
@context[:script].post_message("device #{@context[:device]} is encrypted and mounted")
|
115
|
+
MountedAndActivatedState.new(@context)
|
116
|
+
end
|
117
|
+
|
118
|
+
def calc_device_name
|
119
|
+
dev = @context[:device_name].gsub(/[-]/,"--")
|
120
|
+
"/dev/mapper/vg--#{dev}-lv--#{dev}"
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
# The encrypted storages is mounted and activated. Cleanup and done.
|
126
|
+
class MountedAndActivatedState < DmEncryptState
|
127
|
+
def enter
|
128
|
+
cleanup()
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
def cleanup()
|
133
|
+
@context[:script].post_message("disconnecting...")
|
134
|
+
@logger.debug "MountedAndActivatedState.cleanup"
|
135
|
+
@context[:remote_command_handler].disconnect()
|
136
|
+
@context[:script].post_message("done")
|
137
|
+
DoneState.new(@context)
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
class DoneState < DmEncryptState
|
143
|
+
def done?
|
144
|
+
true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
206
148
|
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require "help/script_execution_state"
|
2
|
+
require "scripts/ec2/ec2_script"
|
3
|
+
require "help/remote_command_handler"
|
4
|
+
require "help/dm_crypt_helper"
|
5
|
+
require "AWS"
|
6
|
+
|
7
|
+
# Script to download a specific snapshot as ZIP
|
8
|
+
# * create a specific instance (with Apache Server),
|
9
|
+
# * create a volume based on the snapshot
|
10
|
+
# * attach the volume
|
11
|
+
# * create a XSF-file-system
|
12
|
+
# * freeze the file-system
|
13
|
+
# * zip the file-system and copy it to the apache folder
|
14
|
+
# * wait 5 minutes (now the zip-file can be downloaded)
|
15
|
+
# * alternatively: copy it to S3 and make it downloadable
|
16
|
+
# * alternatively: copy it to an FTP server
|
17
|
+
#
|
18
|
+
class DownloadSnapshot < Ec2Script
|
19
|
+
# context information needed
|
20
|
+
# * the EC2 credentials (see #Ec2Script)
|
21
|
+
# * ami_id: the ID of the AMI to be started to perform the operations and to run the web-server for download
|
22
|
+
# * security_group_name => name of the security group used to start the AMI (should open ports for SSH and HTTP)
|
23
|
+
# * ssh_key_data => Key information for the security group that starts the AMI [if not set, use ssh_key_files]
|
24
|
+
# * ssh_key_files => Key information for the security group that starts the AMI
|
25
|
+
# * snapshot_id => The ID of the snapshot to be downloaded
|
26
|
+
# * wait_time (optional, default = 300) => time in sec during which the zipped snapshot is downloadable
|
27
|
+
# * zip_file_dest (optional, default = '/var/www/html') => path of directory where the zipped volume is copied to
|
28
|
+
# * zip_file_name (option, default = 'download') => name of the zip-file to download
|
29
|
+
def initialize(input_params)
|
30
|
+
super(input_params)
|
31
|
+
end
|
32
|
+
|
33
|
+
def check_input_parameters()
|
34
|
+
if @input_params[:temp_device_name] == nil
|
35
|
+
@input_params[:temp_device_name] = "/dev/sdj"
|
36
|
+
end
|
37
|
+
if @input_params[:zip_file_dest] == nil
|
38
|
+
@input_params[:zip_file_dest] = "/var/www/html/"
|
39
|
+
end
|
40
|
+
if @input_params[:zip_file_name] == nil
|
41
|
+
@input_params[:zip_file_name] = "download"
|
42
|
+
end
|
43
|
+
if @input_params[:wait_time] == nil
|
44
|
+
@input_params[:wait_time] = 300
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Load the initial state for the script.
|
49
|
+
# Abstract method to be implemented by extending classes.
|
50
|
+
def load_initial_state()
|
51
|
+
DownloadSnapshotState.load_state(@input_params)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Here begins the state machine implementation
|
57
|
+
class DownloadSnapshotState < ScriptExecutionState
|
58
|
+
|
59
|
+
def self.load_state(context)
|
60
|
+
InitialState.new(context)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
#Connected.
|
65
|
+
# Start state. First thing to do is to launch the instance.
|
66
|
+
class InitialState < DownloadSnapshotState
|
67
|
+
def enter
|
68
|
+
launch_instance()
|
69
|
+
InstanceLaunchedState.new(context)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Instance Launched. Create a volume based on the snapshot.
|
74
|
+
class InstanceLaunchedState < DownloadSnapshotState
|
75
|
+
def enter
|
76
|
+
create_volume_from_snapshot()
|
77
|
+
VolumeCreated.new(@context)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
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
|
+
def enter
|
93
|
+
connect()
|
94
|
+
create_fs()
|
95
|
+
FileSystemCreated.new(@context)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# File system created. Mount it.
|
100
|
+
class FileSystemCreated < DownloadSnapshotState
|
101
|
+
def enter
|
102
|
+
mount_fs()
|
103
|
+
FileSystemMounted.new(@context)
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
# File System mounted. Zip the complete directory on the EBS.
|
109
|
+
class FileSystemMounted < DownloadSnapshotState
|
110
|
+
def enter
|
111
|
+
zip_volume()
|
112
|
+
VolumeZippedAndDownloadableState.new(@context)
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
# Volume is zipped and downloadable. Wait 5 minutes.
|
118
|
+
class VolumeZippedAndDownloadableState < DownloadSnapshotState
|
119
|
+
def enter
|
120
|
+
wait_some_time()
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_link
|
124
|
+
"http://#{@context[:dns_name]}/#{@context[:zip_file_name]}.zip"
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def wait_some_time
|
130
|
+
@context[:script].post_message("The snapshot can be downloaded during #{@context[:wait_time]} seconds from link: #{get_link()}")
|
131
|
+
sleep(@context[:wait_time])
|
132
|
+
DownloadStoppedState.new(@context)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Snapshot can no longer be downloaded. Shut down the instance.
|
137
|
+
class DownloadStoppedState < DownloadSnapshotState
|
138
|
+
def enter
|
139
|
+
shut_down_instance()
|
140
|
+
InstanceShutDown.new(@context)
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
# Instance is shut down. Delete the volume created.
|
146
|
+
class InstanceShutDown < DownloadSnapshotState
|
147
|
+
def enter
|
148
|
+
delete_volume()
|
149
|
+
Done.new(@context)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|