CloudyScripts 0.0.8 → 0.0.9
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 +204 -204
- data/lib/help/ec2_helper.rb +54 -54
- data/lib/help/remote_command_handler.rb +203 -190
- data/lib/help/script_execution_state.rb +97 -95
- data/lib/help/state_change_listener.rb +13 -13
- data/lib/scripts/ec2/ami2_ebs_conversion.rb +446 -442
- data/lib/scripts/ec2/dm_encrypt.rb +194 -190
- data/lib/scripts/ec2/ec2_script.rb +41 -41
- metadata +2 -2
@@ -1,191 +1,195 @@
|
|
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
|
-
|
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
|
-
@result[:
|
56
|
-
|
57
|
-
@result[:
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
@result[:
|
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
|
-
|
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
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
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
|
+
# Executes the script.
|
35
|
+
def start_script
|
36
|
+
begin
|
37
|
+
# optional parameters and initialization
|
38
|
+
if @input_params[:ec2_api_server] == nil
|
39
|
+
@input_params[:ec2_api_server] = "us-east-1.ec2.amazonaws.com"
|
40
|
+
end
|
41
|
+
if @input_params[:remote_command_handler] == nil
|
42
|
+
@input_params[:remote_command_handler] = RemoteCommandHandler.new
|
43
|
+
end
|
44
|
+
if @input_params[:ec2_api_handler] == nil
|
45
|
+
@input_params[:ec2_api_handler] = AWS::EC2::Base.new(:access_key_id => @input_params[:aws_access_key],
|
46
|
+
:secret_access_key => @input_params[:aws_secret_key], :server => @input_params[:ec2_api_server])
|
47
|
+
end
|
48
|
+
# start state machine
|
49
|
+
current_state = DmEncryptState.load_state(@input_params)
|
50
|
+
@state_change_listeners.each() {|listener|
|
51
|
+
current_state.register_state_change_listener(listener)
|
52
|
+
}
|
53
|
+
end_state = current_state.start_state_machine()
|
54
|
+
if end_state.failed?
|
55
|
+
@result[:failed] = true
|
56
|
+
@result[:failure_reason] = current_state.end_state.failure_reason
|
57
|
+
@result[:end_state] = current_state.end_state
|
58
|
+
else
|
59
|
+
@result[:failed] = false
|
60
|
+
end
|
61
|
+
rescue Exception => e
|
62
|
+
@logger.warn "exception during encryption: #{e}"
|
63
|
+
@logger.info e.backtrace.join("\n")
|
64
|
+
err = e.to_s
|
65
|
+
err += " (in #{current_state.end_state.to_s})" unless current_state == nil
|
66
|
+
@result[:failed] = true
|
67
|
+
@result[:failure_reason] = err
|
68
|
+
@result[:end_state] = current_state.end_state unless current_state == nil
|
69
|
+
ensure
|
70
|
+
begin
|
71
|
+
@input_params[:remote_command_handler].disconnect
|
72
|
+
rescue Exception => e2
|
73
|
+
end
|
74
|
+
end
|
75
|
+
#
|
76
|
+
@result[:done] = true
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns a hash with the following information:
|
80
|
+
# :done => if execution is done
|
81
|
+
#
|
82
|
+
def get_execution_result
|
83
|
+
@result
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# Here begins the state machine implementation
|
89
|
+
class DmEncryptState < ScriptExecutionState
|
90
|
+
|
91
|
+
def self.load_state(context)
|
92
|
+
InitialState.new(context)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Starting state. Tries to connect via ssh.
|
97
|
+
class InitialState < DmEncryptState
|
98
|
+
def enter
|
99
|
+
connect()
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def connect()
|
105
|
+
@logger.debug "InitialState.connect"
|
106
|
+
if @context[:ssh_key_file] != nil
|
107
|
+
@context[:remote_command_handler].connect_with_keyfile(@context[:ip_address], @context[:ssh_key_file])
|
108
|
+
elsif @context[:ssh_key_data] != nil
|
109
|
+
@context[:remote_command_handler].connect(@context[:ip_address], "root", @context[:ssh_key_data])
|
110
|
+
else
|
111
|
+
raise Exception.new("no key information specified")
|
112
|
+
end
|
113
|
+
@context[:result][:os] = @context[:remote_command_handler].retrieve_os()
|
114
|
+
ConnectedState.new(@context)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Connected via SSH. Tries to install dm-encrypt.#TODO: depends on OS
|
119
|
+
class ConnectedState < DmEncryptState
|
120
|
+
def enter
|
121
|
+
install_tools()
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
def install_tools
|
126
|
+
@logger.debug "ConnectedState.install_tools"
|
127
|
+
if !tools_installed?
|
128
|
+
TOOLS.each() {|tool|
|
129
|
+
@context[:remote_command_handler].install(tool)
|
130
|
+
}
|
131
|
+
end
|
132
|
+
if !@context[:remote_command_handler].remote_execute("modprobe dm_crypt")
|
133
|
+
raise Exception.new("dm-crypt module missing")
|
134
|
+
end
|
135
|
+
if tools_installed?
|
136
|
+
@logger.debug "system says that tools are installed"
|
137
|
+
ToolInstalledState.new(@context)
|
138
|
+
else
|
139
|
+
FailedState.new(@context, "Installation of Tools failed", ConnectedState.new(@context))
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def tools_installed?
|
144
|
+
CHECK.each() {|tool|
|
145
|
+
if !@context[:remote_command_handler].tools_installed?(tool)
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
}
|
149
|
+
true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Connected and Tools installed. Start encryption.
|
154
|
+
class ToolInstalledState < DmEncryptState
|
155
|
+
def enter
|
156
|
+
create_encrypted_volume()
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
def create_encrypted_volume
|
161
|
+
@logger.debug "ToolInstalledState.create_encrypted_volume"
|
162
|
+
@context[:remote_command_handler].encrypt_storage(@context[:device_name],
|
163
|
+
@context[:paraphrase], @context[:device], @context[:storage_path])
|
164
|
+
MountedAndActivatedState.new(@context)
|
165
|
+
end
|
166
|
+
|
167
|
+
def calc_device_name
|
168
|
+
dev = @context[:device_name].gsub(/[-]/,"--")
|
169
|
+
"/dev/mapper/vg--#{dev}-lv--#{dev}"
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
# The encrypted storages is mounted and activated. Cleanup and done.
|
175
|
+
class MountedAndActivatedState < DmEncryptState
|
176
|
+
def enter
|
177
|
+
cleanup()
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
def cleanup()
|
182
|
+
@logger.debug "MountedAndActivatedState.cleanup"
|
183
|
+
@context[:remote_command_handler].disconnect()
|
184
|
+
DoneState.new(@context)
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
class DoneState < DmEncryptState
|
190
|
+
def done?
|
191
|
+
true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
191
195
|
end
|
@@ -1,41 +1,41 @@
|
|
1
|
-
# Base class for any script on EC2.
|
2
|
-
class Ec2Script
|
3
|
-
# Initialization. Common Input parameters:
|
4
|
-
# * aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
|
5
|
-
# * aws_secret_key => the Amazon AWS Secret Key
|
6
|
-
# * ec2_api_server => the API Server to connect to (optional, default is us-east-1 (=> <ec2_api_server>.ec2.amazonaws.com)
|
7
|
-
# * logger => allows to pass a ruby logger object used for logging (optional, default is a stdout logger with level WARN)
|
8
|
-
# Scripts may add specific key/value pairs.
|
9
|
-
def initialize(input_params)
|
10
|
-
@input_params = input_params
|
11
|
-
@state_change_listeners = []
|
12
|
-
if input_params[:logger] == nil
|
13
|
-
@logger = Logger.new(STDOUT)
|
14
|
-
@logger .level = Logger::WARN
|
15
|
-
input_params[:logger] = @logger
|
16
|
-
end
|
17
|
-
@result = {:done => false}
|
18
|
-
@input_params[:result] = @result
|
19
|
-
end
|
20
|
-
|
21
|
-
def register_state_change_listener(listener)
|
22
|
-
@state_change_listeners << listener
|
23
|
-
end
|
24
|
-
|
25
|
-
def start_script
|
26
|
-
raise Exception.new("must be implemented")
|
27
|
-
end
|
28
|
-
|
29
|
-
# Return a hash of results. Common values are:
|
30
|
-
# * :done => is true when the script has terminated, otherwise false
|
31
|
-
# * :failed => is false when the script succeeded
|
32
|
-
# * :failure_reason => returns a failure reason (string)
|
33
|
-
# * :end_state => returns the state, in which the script terminated (#Help::ScriptExecutionState)
|
34
|
-
# Scripts may add specific key/value pairs.
|
35
|
-
# *
|
36
|
-
def get_execution_result
|
37
|
-
raise Exception.new("must be implemented")
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
1
|
+
# Base class for any script on EC2.
|
2
|
+
class Ec2Script
|
3
|
+
# Initialization. Common Input parameters:
|
4
|
+
# * aws_access_key => the Amazon AWS Access Key (see Your Account -> Security Credentials)
|
5
|
+
# * aws_secret_key => the Amazon AWS Secret Key
|
6
|
+
# * ec2_api_server => the API Server to connect to (optional, default is us-east-1 (=> <ec2_api_server>.ec2.amazonaws.com)
|
7
|
+
# * logger => allows to pass a ruby logger object used for logging (optional, default is a stdout logger with level WARN)
|
8
|
+
# Scripts may add specific key/value pairs.
|
9
|
+
def initialize(input_params)
|
10
|
+
@input_params = input_params
|
11
|
+
@state_change_listeners = []
|
12
|
+
if input_params[:logger] == nil
|
13
|
+
@logger = Logger.new(STDOUT)
|
14
|
+
@logger .level = Logger::WARN
|
15
|
+
input_params[:logger] = @logger
|
16
|
+
end
|
17
|
+
@result = {:done => false, :failed => false}
|
18
|
+
@input_params[:result] = @result
|
19
|
+
end
|
20
|
+
|
21
|
+
def register_state_change_listener(listener)
|
22
|
+
@state_change_listeners << listener
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_script
|
26
|
+
raise Exception.new("must be implemented")
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return a hash of results. Common values are:
|
30
|
+
# * :done => is true when the script has terminated, otherwise false
|
31
|
+
# * :failed => is false when the script succeeded
|
32
|
+
# * :failure_reason => returns a failure reason (string)
|
33
|
+
# * :end_state => returns the state, in which the script terminated (#Help::ScriptExecutionState)
|
34
|
+
# Scripts may add specific key/value pairs.
|
35
|
+
# *
|
36
|
+
def get_execution_result
|
37
|
+
raise Exception.new("must be implemented")
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|