openstudio-aws 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +14 -13
- data/lib/openstudio-aws.rb +9 -7
- data/lib/openstudio/aws/aws.rb +53 -40
- data/lib/openstudio/aws/config.rb +0 -1
- data/lib/openstudio/aws/version.rb +2 -2
- data/lib/openstudio/lib/ami_list.rb +79 -0
- data/lib/openstudio/lib/openstudio_aws_instance.rb +138 -0
- data/lib/openstudio/lib/openstudio_aws_logger.rb +61 -0
- data/lib/openstudio/lib/openstudio_aws_methods.rb +232 -0
- data/lib/openstudio/lib/openstudio_aws_wrapper.rb +315 -0
- data/lib/openstudio/lib/os-aws.rb +77 -351
- data/spec/aws_instances/aws_spec_api.rb +70 -0
- data/spec/openstudio-aws/ami_list_spec.rb +37 -0
- data/spec/openstudio-aws/aws_methods_spec.rb +26 -0
- data/spec/openstudio-aws/lib_spec.rb +48 -0
- data/spec/reports/SPEC-OpenStudio-Aws-Aws-create-a-new-instance.xml +43 -0
- data/spec/reports/SPEC-OpenStudio-Aws-Aws-workers-before-server.xml +9 -0
- data/spec/reports/SPEC-OpenStudio-Aws-Aws.xml +7 -0
- data/spec/reports/SPEC-OpenStudio-Aws-Config-create-a-new-config.0.xml +9 -0
- data/spec/reports/SPEC-OpenStudio-Aws-Config-create-a-new-config.xml +9 -0
- data/spec/reports/SPEC-OpenStudio-Aws-Config.0.xml +7 -0
- data/spec/reports/SPEC-OpenStudio-Aws-Config.xml +7 -0
- data/spec/reports/SPEC-OpenStudioAmis-version-1.0.xml +13 -0
- data/spec/reports/SPEC-OpenStudioAmis-version-1.xml +13 -0
- data/spec/reports/SPEC-OpenStudioAmis.0.xml +7 -0
- data/spec/reports/SPEC-OpenStudioAmis.xml +7 -0
- data/spec/reports/SPEC-OpenStudioAwsMethods-processors.0.xml +11 -0
- data/spec/reports/SPEC-OpenStudioAwsMethods-processors.xml +11 -0
- data/spec/reports/SPEC-OpenStudioAwsMethods.0.xml +7 -0
- data/spec/reports/SPEC-OpenStudioAwsMethods.xml +7 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper-authenticated-session-availability.0.xml +11 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper-authenticated-session-availability.xml +11 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper-authenticated-session-new-instance.0.xml +9 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper-authenticated-session-new-instance.xml +9 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper-authenticated-session.0.xml +7 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper-authenticated-session.xml +7 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper-unauthenticated-session.0.xml +9 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper-unauthenticated-session.xml +9 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper.0.xml +7 -0
- data/spec/reports/SPEC-OpenStudioAwsWrapper.xml +7 -0
- metadata +70 -10
- data/lib/openstudio/aws/send_data.rb +0 -42
- data/spec/openstudio-aws/aws_spec.rb +0 -53
@@ -0,0 +1,61 @@
|
|
1
|
+
# NOTE: Do not modify this file as it is copied over. Modify the source file and rerun rake import_files
|
2
|
+
######################################################################
|
3
|
+
# Copyright (c) 2008-2014, Alliance for Sustainable Energy.
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This library is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the GNU Lesser General Public
|
8
|
+
# License as published by the Free Software Foundation; either
|
9
|
+
# version 2.1 of the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This library is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this library; if not, write to the Free Software
|
18
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
19
|
+
######################################################################
|
20
|
+
|
21
|
+
######################################################################
|
22
|
+
# == Synopsis
|
23
|
+
#
|
24
|
+
# Uses the aws-sdk gem to communicate with AWS
|
25
|
+
#
|
26
|
+
# == Usage
|
27
|
+
#
|
28
|
+
# ruby aws.rb access_key secret_key us-east-1 EC2 launch_server "{\"instance_type\":\"t1.micro\"}"
|
29
|
+
#
|
30
|
+
# ARGV[0] - Access Key
|
31
|
+
# ARGV[1] - Secret Key
|
32
|
+
# ARGV[2] - Region
|
33
|
+
# ARGV[3] - Service (e.g. "EC2" or "CloudWatch")
|
34
|
+
# ARGV[4] - Command (e.g. "launch_server")
|
35
|
+
# ARGV[5] - Optional json with parameters associated with command
|
36
|
+
#
|
37
|
+
######################################################################
|
38
|
+
|
39
|
+
require 'logger'
|
40
|
+
|
41
|
+
# module for logging
|
42
|
+
module Logging
|
43
|
+
def logger
|
44
|
+
@logger ||= Logging.logger_for(self.class.name)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Use a hash class-ivar to cache a unique Logger per class:
|
48
|
+
@loggers = {}
|
49
|
+
|
50
|
+
class << self
|
51
|
+
def logger_for(classname)
|
52
|
+
@loggers[classname] ||= configure_logger_for(classname)
|
53
|
+
end
|
54
|
+
|
55
|
+
def configure_logger_for(classname)
|
56
|
+
logger = Logger.new(File.expand_path("~/.aws.log"))
|
57
|
+
logger.progname = classname
|
58
|
+
logger
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
# NOTE: Do not modify this file as it is copied over. Modify the source file and rerun rake import_files
|
2
|
+
######################################################################
|
3
|
+
# Copyright (c) 2008-2014, Alliance for Sustainable Energy.
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This library is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the GNU Lesser General Public
|
8
|
+
# License as published by the Free Software Foundation; either
|
9
|
+
# version 2.1 of the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This library is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this library; if not, write to the Free Software
|
18
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
19
|
+
######################################################################
|
20
|
+
|
21
|
+
######################################################################
|
22
|
+
# == Synopsis
|
23
|
+
#
|
24
|
+
# Methods module for openstudio aws
|
25
|
+
#
|
26
|
+
# == Usage
|
27
|
+
#
|
28
|
+
# Inside the class in which this file is included make sure to implement the following
|
29
|
+
#
|
30
|
+
# Member Variables:
|
31
|
+
# private_key : the in memory private key
|
32
|
+
# logger : logger class in which to write log messages
|
33
|
+
#
|
34
|
+
######################################################################
|
35
|
+
|
36
|
+
module OpenStudioAwsMethods
|
37
|
+
def find_processors(instance)
|
38
|
+
lookup = {
|
39
|
+
"m3.xlarge" => 4,
|
40
|
+
"m3.2xlarge" => 8,
|
41
|
+
"m1.small" => 1,
|
42
|
+
"m1.medium" => 1,
|
43
|
+
"m1.large" => 2,
|
44
|
+
"m1.xlarge" => 4,
|
45
|
+
"c3.large" => 2,
|
46
|
+
"c3.xlarge" => 4,
|
47
|
+
"c3.2xlarge" => 8,
|
48
|
+
"c3.4xlarge" => 16,
|
49
|
+
"c3.8xlarge" => 16,
|
50
|
+
"c1.medium" => 2,
|
51
|
+
"c1.xlarge" => 8,
|
52
|
+
"cc2.8xlarge" => 16,
|
53
|
+
"g2.2xlarge" => 8,
|
54
|
+
"cg1.4xlarge" => 16,
|
55
|
+
"m2.xlarge" => 2,
|
56
|
+
"m2.2xlarge" => 4,
|
57
|
+
"m2.4xlarge" => 8,
|
58
|
+
"cr1.8xlarge" => 16,
|
59
|
+
"hi1.4xlarge" => 16,
|
60
|
+
"hs1.8xlarge" => 16,
|
61
|
+
"t1.micro" => 1,
|
62
|
+
}
|
63
|
+
|
64
|
+
processors = 1
|
65
|
+
if lookup.has_key?(instance)
|
66
|
+
processors = lookup[instance]
|
67
|
+
else
|
68
|
+
#logger.warn "Could not find the number of processors for instance type of #{instance}" if logger
|
69
|
+
end
|
70
|
+
|
71
|
+
processors
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def upload_file(host, local_path, remote_path)
|
76
|
+
retries = 0
|
77
|
+
begin
|
78
|
+
Net::SCP.start(host, 'ubuntu', :key_data => [@private_key]) do |scp|
|
79
|
+
scp.upload! local_path, remote_path
|
80
|
+
end
|
81
|
+
rescue SystemCallError, Timeout::Error => e
|
82
|
+
# port 22 might not be available immediately after the instance finishes launching
|
83
|
+
return if retries == 5
|
84
|
+
retries += 1
|
85
|
+
sleep 1
|
86
|
+
retry
|
87
|
+
rescue
|
88
|
+
# Unknown upload error, retry
|
89
|
+
return if retries == 5
|
90
|
+
retries += 1
|
91
|
+
sleep 1
|
92
|
+
retry
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def send_command(host, command)
|
98
|
+
#retries = 0
|
99
|
+
begin
|
100
|
+
output = ''
|
101
|
+
Net::SSH.start(host, 'ubuntu', :key_data => [@private_key]) do |ssh|
|
102
|
+
response = ssh.exec!(command)
|
103
|
+
output += response if !response.nil?
|
104
|
+
end
|
105
|
+
return output
|
106
|
+
rescue Net::SSH::HostKeyMismatch => e
|
107
|
+
e.remember_host!
|
108
|
+
# key mismatch, retry
|
109
|
+
#return if retries == 5
|
110
|
+
#retries += 1
|
111
|
+
sleep 1
|
112
|
+
retry
|
113
|
+
rescue Net::SSH::AuthenticationFailed
|
114
|
+
error(-1, "Incorrect private key")
|
115
|
+
rescue SystemCallError, Timeout::Error => e
|
116
|
+
# port 22 might not be available immediately after the instance finishes launching
|
117
|
+
#return if retries == 5
|
118
|
+
#retries += 1
|
119
|
+
sleep 1
|
120
|
+
retry
|
121
|
+
rescue Exception => e
|
122
|
+
puts e.message
|
123
|
+
puts e.backtrace.inspect
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
#======================= send command ======================#
|
128
|
+
# Send a command through SSH Shell to an instance.
|
129
|
+
# Need to pass instance object and the command as a string.
|
130
|
+
def shell_command(host, command)
|
131
|
+
begin
|
132
|
+
@logger.info("ssh_command #{command}")
|
133
|
+
Net::SSH.start(host, 'ubuntu', :key_data => [@private_key]) do |ssh|
|
134
|
+
channel = ssh.open_channel do |ch|
|
135
|
+
ch.exec "#{command}" do |ch, success|
|
136
|
+
raise "could not execute #{command}" unless success
|
137
|
+
|
138
|
+
# "on_data" is called when the process writes something to stdout
|
139
|
+
ch.on_data do |c, data|
|
140
|
+
#$stdout.print data
|
141
|
+
@logger.info("#{data.inspect}")
|
142
|
+
end
|
143
|
+
|
144
|
+
# "on_extended_data" is called when the process writes something to stderr
|
145
|
+
ch.on_extended_data do |c, type, data|
|
146
|
+
#$stderr.print data
|
147
|
+
@logger.info("#{data.inspect}")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
rescue Net::SSH::HostKeyMismatch => e
|
153
|
+
e.remember_host!
|
154
|
+
@logger.info("key mismatch, retry")
|
155
|
+
sleep 1
|
156
|
+
retry
|
157
|
+
rescue SystemCallError, Timeout::Error => e
|
158
|
+
# port 22 might not be available immediately after the instance finishes launching
|
159
|
+
sleep 1
|
160
|
+
@logger.info("Not Yet")
|
161
|
+
retry
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def wait_command(host, command)
|
166
|
+
begin
|
167
|
+
flag = 0
|
168
|
+
while flag == 0 do
|
169
|
+
@logger.info("wait_command #{command}")
|
170
|
+
Net::SSH.start(host, 'ubuntu', :key_data => [@private_key]) do |ssh|
|
171
|
+
channel = ssh.open_channel do |ch|
|
172
|
+
ch.exec "#{command}" do |ch, success|
|
173
|
+
raise "could not execute #{command}" unless success
|
174
|
+
|
175
|
+
# "on_data" is called when the process writes something to stdout
|
176
|
+
ch.on_data do |c, data|
|
177
|
+
@logger.info("#{data.inspect}")
|
178
|
+
if data.chomp == "true"
|
179
|
+
@logger.info("wait_command #{command} is true")
|
180
|
+
flag = 1
|
181
|
+
else
|
182
|
+
sleep 5
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# "on_extended_data" is called when the process writes something to stderr
|
187
|
+
ch.on_extended_data do |c, type, data|
|
188
|
+
@logger.info("#{data.inspect}")
|
189
|
+
if data == "true"
|
190
|
+
@logger.info("wait_command #{command} is true")
|
191
|
+
flag = 1
|
192
|
+
else
|
193
|
+
sleep 5
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
rescue Net::SSH::HostKeyMismatch => e
|
201
|
+
e.remember_host!
|
202
|
+
@logger.info("key mismatch, retry")
|
203
|
+
sleep 5
|
204
|
+
retry
|
205
|
+
rescue SystemCallError, Timeout::Error => e
|
206
|
+
# port 22 might not be available immediately after the instance finishes launching
|
207
|
+
sleep 5
|
208
|
+
@logger.info("Not Yet")
|
209
|
+
retry
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def download_file(host, remote_path, local_path)
|
214
|
+
retries = 0
|
215
|
+
begin
|
216
|
+
Net::SCP.start(host, 'ubuntu', :key_data => [@private_key]) do |scp|
|
217
|
+
scp.download! remote_path, local_path
|
218
|
+
end
|
219
|
+
rescue SystemCallError, Timeout::Error => e
|
220
|
+
# port 22 might not be available immediately after the instance finishes launching
|
221
|
+
return if retries == 5
|
222
|
+
retries += 1
|
223
|
+
sleep 1
|
224
|
+
retry
|
225
|
+
rescue
|
226
|
+
return if retries == 5
|
227
|
+
retries += 1
|
228
|
+
sleep 1
|
229
|
+
retry
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,315 @@
|
|
1
|
+
# NOTE: Do not modify this file as it is copied over. Modify the source file and rerun rake import_files
|
2
|
+
######################################################################
|
3
|
+
# Copyright (c) 2008-2014, Alliance for Sustainable Energy.
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This library is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the GNU Lesser General Public
|
8
|
+
# License as published by the Free Software Foundation; either
|
9
|
+
# version 2.1 of the License, or (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This library is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# Lesser General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Lesser General Public
|
17
|
+
# License along with this library; if not, write to the Free Software
|
18
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
19
|
+
######################################################################
|
20
|
+
|
21
|
+
######################################################################
|
22
|
+
# == Synopsis
|
23
|
+
#
|
24
|
+
# Uses the aws-sdk gem to communicate with AWS
|
25
|
+
#
|
26
|
+
# == Usage
|
27
|
+
#
|
28
|
+
# ruby aws.rb access_key secret_key us-east-1 EC2 launch_server "{\"instance_type\":\"t1.micro\"}"
|
29
|
+
#
|
30
|
+
# ARGV[0] - Access Key
|
31
|
+
# ARGV[1] - Secret Key
|
32
|
+
# ARGV[2] - Region
|
33
|
+
# ARGV[3] - Service (e.g. "EC2" or "CloudWatch")
|
34
|
+
# ARGV[4] - Command (e.g. "launch_server")
|
35
|
+
# ARGV[5] - Optional json with parameters associated with command
|
36
|
+
#
|
37
|
+
######################################################################
|
38
|
+
|
39
|
+
require_relative 'openstudio_aws_logger'
|
40
|
+
|
41
|
+
class OpenStudioAwsWrapper
|
42
|
+
include Logging
|
43
|
+
|
44
|
+
attr_reader :group_uuid
|
45
|
+
attr_reader :security_group_name
|
46
|
+
attr_reader :key_pair_name
|
47
|
+
attr_reader :server
|
48
|
+
attr_reader :workers
|
49
|
+
|
50
|
+
def initialize(credentials = nil, group_uuid = nil)
|
51
|
+
@group_uuid = group_uuid || Time.now.to_i.to_s
|
52
|
+
|
53
|
+
@security_group_name = nil
|
54
|
+
@key_pair_name = nil
|
55
|
+
@private_key = nil
|
56
|
+
@server = nil
|
57
|
+
@workers = []
|
58
|
+
|
59
|
+
# If you already set the credentials in another script in memory, then you won't have to do it here, but
|
60
|
+
# it won't hurt if you do
|
61
|
+
Aws.config = credentials if credentials
|
62
|
+
@aws = Aws::EC2.new
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_or_retrieve_security_group(sg_name = nil)
|
66
|
+
tmp_name = sg_name || 'openstudio-server-sg-v1'
|
67
|
+
group = @aws.describe_security_groups({:filters => [{:name => 'group-name', :values => [tmp_name]}]})
|
68
|
+
logger.info "Length of the security group is: #{group.data.security_groups.length}"
|
69
|
+
if group.data.security_groups.length == 0
|
70
|
+
logger.info "server group not found --- will create a new one"
|
71
|
+
@aws.create_security_group({:group_name => tmp_name, :description => "group dynamically created by #{__FILE__}"})
|
72
|
+
@aws.authorize_security_group_ingress(
|
73
|
+
{
|
74
|
+
:group_name => tmp_name,
|
75
|
+
:ip_permissions => [
|
76
|
+
{:ip_protocol => 'tcp', :from_port => 1, :to_port => 65535, :ip_ranges => [:cidr_ip => "0.0.0.0/0"]}
|
77
|
+
]
|
78
|
+
}
|
79
|
+
)
|
80
|
+
@aws.authorize_security_group_ingress(
|
81
|
+
{
|
82
|
+
:group_name => tmp_name,
|
83
|
+
:ip_permissions => [
|
84
|
+
{:ip_protocol => 'icmp', :from_port => -1, :to_port => -1, :ip_ranges => [:cidr_ip => "0.0.0.0/0"]
|
85
|
+
}
|
86
|
+
]
|
87
|
+
}
|
88
|
+
)
|
89
|
+
|
90
|
+
# reload group information
|
91
|
+
group = @aws.describe_security_groups({:filters => [{:name => 'group-name', :values => [tmp_name]}]})
|
92
|
+
end
|
93
|
+
@security_group_name = group.data.security_groups.first.group_name
|
94
|
+
logger.info("server_group #{group.data.security_groups.first.group_name}")
|
95
|
+
end
|
96
|
+
|
97
|
+
def describe_availability_zones
|
98
|
+
resp = @aws.describe_availability_zones
|
99
|
+
map = []
|
100
|
+
resp.data.availability_zones.each do |zn|
|
101
|
+
map << zn.to_hash
|
102
|
+
end
|
103
|
+
|
104
|
+
{:availability_zone_info => map}
|
105
|
+
end
|
106
|
+
|
107
|
+
def describe_availability_zones_json
|
108
|
+
describe_availability_zones.to_json
|
109
|
+
end
|
110
|
+
|
111
|
+
def describe_total_instances
|
112
|
+
resp = @aws.describe_instance_status
|
113
|
+
|
114
|
+
region = resp.instance_statuses.length > 0 ? resp.instance_statuses.first.availability_zone : "no_instances"
|
115
|
+
{:total_instances => resp.instance_statuses.length, :region => region}
|
116
|
+
end
|
117
|
+
|
118
|
+
def describe_total_instances_json
|
119
|
+
describe_total_instances.to_json
|
120
|
+
end
|
121
|
+
|
122
|
+
# return all of the running instances, or filter by the group_uuid & instance type
|
123
|
+
def describe_running_instances(group_uuid = nil, openstudio_instance_type = nil)
|
124
|
+
|
125
|
+
resp = nil
|
126
|
+
if group_uuid
|
127
|
+
resp = @aws.describe_instances(
|
128
|
+
{
|
129
|
+
:filters => [
|
130
|
+
{:name => "instance-state-code", :values => [0.to_s, 16.to_s]}, #running or pending
|
131
|
+
{:name => "tag-key", :values => ["GroupUUID"]},
|
132
|
+
{:name => "tag-value", :values => [group_uuid.to_s]} # todo: how to check for the server versions
|
133
|
+
#{:name => "tag-value", :values => [group_uuid.to_s, "OpenStudio#{@openstudio_instance_type.capitalize}"]}
|
134
|
+
#{:name => "tag:key=value", :values => ["GroupUUID=#{group_uuid.to_s}"]}
|
135
|
+
]
|
136
|
+
}
|
137
|
+
)
|
138
|
+
else
|
139
|
+
# todo: need to restrict this to only the current user
|
140
|
+
resp = @aws.describe_instances()
|
141
|
+
end
|
142
|
+
|
143
|
+
instance_data = nil
|
144
|
+
if resp
|
145
|
+
if resp.reservations.length > 0
|
146
|
+
resp = resp.reservations.first
|
147
|
+
if resp.instances
|
148
|
+
instance_data = []
|
149
|
+
resp.instances.each do |i|
|
150
|
+
instance_data << i.to_hash
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
end
|
155
|
+
else
|
156
|
+
logger.info "no running instances found"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
instance_data
|
161
|
+
end
|
162
|
+
|
163
|
+
def create_or_retrieve_key_pair(key_pair_name = nil, private_key_file = nil)
|
164
|
+
tmp_name = key_pair_name || "os-key-pair-#{@group_uuid}"
|
165
|
+
|
166
|
+
# the describe_key_pairs method will raise an expection if it can't find the keypair, so catch it
|
167
|
+
resp = nil
|
168
|
+
begin
|
169
|
+
resp = @aws.describe_key_pairs({:key_names => [tmp_name]}).data
|
170
|
+
raise "looks like there are 2 key pairs with the same name" if resp.key_pairs.size >= 2
|
171
|
+
rescue
|
172
|
+
logger.info "could not find key pair '#{tmp_name}'"
|
173
|
+
end
|
174
|
+
|
175
|
+
if resp.nil? || resp.key_pairs.size == 0
|
176
|
+
# create the new key_pair
|
177
|
+
# check if the key pair name exists
|
178
|
+
# create a new key pair everytime
|
179
|
+
keypair = @aws.create_key_pair({:key_name => tmp_name})
|
180
|
+
|
181
|
+
# save the private key to memory (which can later be persisted via the save_private_key method)
|
182
|
+
@private_key = keypair.data.key_material
|
183
|
+
@key_pair_name = keypair.data.key_name
|
184
|
+
else
|
185
|
+
logger.info "found existing keypair #{resp.key_pairs.first}"
|
186
|
+
@key_pair_name = resp.key_pairs.first[:key_name]
|
187
|
+
|
188
|
+
if File.exists(private_key_file)
|
189
|
+
@private_key = File.read(private_key_file, 'r')
|
190
|
+
else
|
191
|
+
# should we raise?
|
192
|
+
logger.error "Could not find the private key file to load from #{private_key_file}"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
logger.info("create key pair: #{@key_pair_name}")
|
197
|
+
end
|
198
|
+
|
199
|
+
def save_private_key(filename)
|
200
|
+
if @private_key
|
201
|
+
File.open(filename, 'w') { |f| f << @private_key }
|
202
|
+
File.chmod(0600, filename)
|
203
|
+
else
|
204
|
+
logger.error "no private key found in which to persist"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def launch_server(image_id, instance_type)
|
209
|
+
user_data = File.read(File.expand_path(File.dirname(__FILE__))+'/server_script.sh')
|
210
|
+
@server = OpenStudioAwsInstance.new(@aws, :server, @key_pair_name, @security_group_name, @group_uuid, @private_key)
|
211
|
+
@server.launch_instance(image_id, instance_type, user_data)
|
212
|
+
end
|
213
|
+
|
214
|
+
def launch_workers(image_id, instance_type, num)
|
215
|
+
user_data = File.read(File.expand_path(File.dirname(__FILE__))+'/worker_script.sh.template')
|
216
|
+
user_data.gsub!(/SERVER_IP/, @server.data.ip)
|
217
|
+
user_data.gsub!(/SERVER_HOSTNAME/, 'master')
|
218
|
+
user_data.gsub!(/SERVER_ALIAS/, '')
|
219
|
+
logger.info("worker user_data #{user_data.inspect}")
|
220
|
+
|
221
|
+
# thread the launching of the workers
|
222
|
+
threads = []
|
223
|
+
num.times do
|
224
|
+
@workers << OpenStudioAwsInstance.new(@aws, :worker, @key_pair_name, @security_group_name, @group_uuid, @private_key)
|
225
|
+
threads << Thread.new do
|
226
|
+
@workers.last.launch_instance(image_id, instance_type, user_data)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
threads.each { |t| t.join }
|
230
|
+
|
231
|
+
# todo: do we need to have a flag if the worker node is successful?
|
232
|
+
# todo: do we need to check the current list of running workers?
|
233
|
+
end
|
234
|
+
|
235
|
+
# blocking method that waits for servers and workers to be fully configured (i.e. execution of user_data has
|
236
|
+
# occured on all nodes)
|
237
|
+
def configure_server_and_workers
|
238
|
+
#todo: add a timeout here!
|
239
|
+
logger.info("waiting for server user_data to complete")
|
240
|
+
@server.wait_command(@server.data.ip, '[ -e /home/ubuntu/user_data_done ] && echo "true"')
|
241
|
+
@logger.info("waiting for worker user_data to complete")
|
242
|
+
@workers.each { |worker| worker.wait_command(worker.data.ip, '[ -e /home/ubuntu/user_data_done ] && echo "true"') }
|
243
|
+
|
244
|
+
ips = "master|#{@server.data.ip}|#{@server.data.dns}|#{@server.data.procs}|ubuntu|ubuntu\n"
|
245
|
+
@workers.each { |worker| ips << "worker|#{worker.data.ip}|#{worker.data.dns}|#{worker.data.procs}|ubuntu|ubuntu|true\n" }
|
246
|
+
file = Tempfile.new('ip_addresses')
|
247
|
+
file.write(ips)
|
248
|
+
file.close
|
249
|
+
upload_file(@server.data.ip, file.path, 'ip_addresses')
|
250
|
+
file.unlink
|
251
|
+
logger.info("ips #{ips}")
|
252
|
+
@server.shell_command(@server.data.ip, 'chmod 664 /home/ubuntu/ip_addresses')
|
253
|
+
@server.shell_command(@server.data.ip, '~/setup-ssh-keys.sh')
|
254
|
+
@server.shell_command(@server.data.ip, '~/setup-ssh-worker-nodes.sh ip_addresses')
|
255
|
+
|
256
|
+
mongoid = File.read(File.expand_path(File.dirname(__FILE__))+'/mongoid.yml.template')
|
257
|
+
mongoid.gsub!(/SERVER_IP/, @server.data.ip)
|
258
|
+
file = Tempfile.new('mongoid.yml')
|
259
|
+
file.write(mongoid)
|
260
|
+
file.close
|
261
|
+
@server.upload_file(@server.data.ip, file.path, '/mnt/openstudio/rails-models/mongoid.yml')
|
262
|
+
@workers.each { |worker| worker.upload_file(worker.data.ip, file.path, '/mnt/openstudio/rails-models/mongoid.yml') }
|
263
|
+
file.unlink
|
264
|
+
|
265
|
+
# Does this command crash it?
|
266
|
+
@server.shell_command(@server.data.ip, 'chmod 664 /mnt/openstudio/rails-models/mongoid.yml')
|
267
|
+
@workers.each { |worker| worker.shell_command(worker.data.ip, 'chmod 664 /mnt/openstudio/rails-models/mongoid.yml') }
|
268
|
+
|
269
|
+
true
|
270
|
+
end
|
271
|
+
|
272
|
+
# method to query the amazon api to find the server (if it exists), based on the group id
|
273
|
+
# if it is found, then it will set the @server member variable.
|
274
|
+
# Note that the information around keys and security groups is pulled from the instance information.
|
275
|
+
def find_server(group_uuid = nil)
|
276
|
+
group_uuid = group_uuid || @group_uuid
|
277
|
+
|
278
|
+
logger.info "finding the server for groupid of #{group_uuid}"
|
279
|
+
raise "no group uuid defined either in member variable or method argument" if group_uuid.nil?
|
280
|
+
|
281
|
+
resp = describe_running_instances(group_uuid, :server)
|
282
|
+
if resp
|
283
|
+
raise "more than one server running with group uuid of #{group_uuid} found, expecting only one" if resp.size > 1
|
284
|
+
resp = resp.first
|
285
|
+
if !@server
|
286
|
+
logger.info "Server found and loading data into object [instance id is #{resp[:instance_id]}]"
|
287
|
+
@server = OpenStudioAwsInstance.new(@aws, :server, resp[:key_name], resp[:security_groups].first[:group_name], group_uuid, @private_key)
|
288
|
+
@server.load_instance_data(resp)
|
289
|
+
else
|
290
|
+
logger.info "Server instance is already defined with instance #{resp[:instance_id]}"
|
291
|
+
end
|
292
|
+
else
|
293
|
+
raise "could not find a running server instance"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def to_os_worker_hash
|
298
|
+
worker_hash = []
|
299
|
+
@workers.each { |worker|
|
300
|
+
worker_hash.push({
|
301
|
+
:id => worker.data.id,
|
302
|
+
:ip => 'http://' + worker.data.ip,
|
303
|
+
:dns => worker.data.dns,
|
304
|
+
:procs => worker.data.procs
|
305
|
+
})
|
306
|
+
}
|
307
|
+
|
308
|
+
out = {:workers => worker_hash}
|
309
|
+
logger.info out
|
310
|
+
|
311
|
+
out
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
end
|