openstudio-aws 0.1.7 → 0.1.8
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.
- 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
|