right_api_provision 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +68 -0
- data/Rakefile +16 -0
- data/lib/right_api_provision/api15.rb +227 -0
- data/lib/right_api_provision/exception.rb +31 -0
- data/lib/right_api_provision/provisioner.rb +193 -0
- data/lib/right_api_provision/version.rb +3 -0
- data/lib/right_api_provision.rb +4 -0
- data/right_api_provision.gemspec +21 -0
- data/spec/right_api_provision/api15_spec.rb +262 -0
- data/spec/right_api_provision/client_spec_old.rb +342 -0
- data/spec/right_api_provision/provisioner_spec.rb +27 -0
- data/spec/spec_helper.rb +2 -0
- data/test/provisioner_test.rb +42 -0
- metadata +169 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 caryp
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# RightApiProvision
|
2
|
+
|
3
|
+
Basic helper gem for provisioning IaaS servers using the RightScale API.
|
4
|
+
|
5
|
+
It is intended to be used by ruby applications that need to launch servers.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'right_api_provision'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
See lib/right_api_provisioner/provisioner.rb for all public methods.
|
20
|
+
|
21
|
+
Example:
|
22
|
+
|
23
|
+
require "right_api_provision"
|
24
|
+
|
25
|
+
# initialize rightscale provisioner
|
26
|
+
@rightscale =
|
27
|
+
RightApiProvision::Provisioner.new("my_user@somewhere.com", // user
|
28
|
+
"my_rightscale_password", // password
|
29
|
+
12345) // rightscale account ID
|
30
|
+
|
31
|
+
# setup some inputs
|
32
|
+
server_inputs = {
|
33
|
+
# open up port 8000
|
34
|
+
"sys_firewall/rule/enable" => "text:enable",
|
35
|
+
"sys_firewall/rule/port" => "text:8000",
|
36
|
+
"sys_firewall/rule/ip_address" => "text:any",
|
37
|
+
"sys_firewall/rule/protocol" => "text:tcp"
|
38
|
+
}
|
39
|
+
|
40
|
+
# provision a RightScale managed server from a ServerTemplate
|
41
|
+
@rightscale.provision("My Cool Server",
|
42
|
+
"ServerTemplate for Linux (v13.5)", // name or ID
|
43
|
+
"AWS US-East",
|
44
|
+
"My Deployment",
|
45
|
+
server_inputs)
|
46
|
+
|
47
|
+
# wait for server to be ready
|
48
|
+
state = @rightscale.wait_for_state("operational", 30)
|
49
|
+
if state != "operational"
|
50
|
+
raise "Unexpected state. State: #{state}"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Do stuff with your brand new server...
|
54
|
+
|
55
|
+
|
56
|
+
## TODO
|
57
|
+
|
58
|
+
1. document this readme
|
59
|
+
2. add logger class
|
60
|
+
|
61
|
+
|
62
|
+
## Contributing
|
63
|
+
|
64
|
+
1. Fork it
|
65
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
66
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
67
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
68
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
desc "Run all specs in spec directory"
|
9
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
10
|
+
t.pattern = 'spec/**/*_spec.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
rescue LoadError
|
14
|
+
STDERR.puts "\n*** RSpec not available. (sudo) gem install rspec to run unit tests. ***\n\n"
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,227 @@
|
|
1
|
+
module RightApiProvision
|
2
|
+
class API15
|
3
|
+
|
4
|
+
attr_reader :client
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
require "right_api_client"
|
8
|
+
end
|
9
|
+
|
10
|
+
def connection(email, password, account_id, api_url = nil)
|
11
|
+
begin
|
12
|
+
args = { :email => email, :password => password, :account_id => account_id }
|
13
|
+
@url = api_url
|
14
|
+
args[:api_url] = @url if @url
|
15
|
+
@connection ||= RightApi::Client.new(args)
|
16
|
+
@client = @connection
|
17
|
+
rescue Exception => e
|
18
|
+
args.delete(:password) # don't log password
|
19
|
+
puts "ERROR: could not connect to RightScale API. Params: #{args.inspect}"
|
20
|
+
puts e.message
|
21
|
+
puts e.backtrace
|
22
|
+
raise e
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def user_data(server)
|
27
|
+
@user_data ||= server.show.current_instance(:view=>"extended").show.user_data
|
28
|
+
end
|
29
|
+
|
30
|
+
def data_request_url(userdata)
|
31
|
+
data_hash = {}
|
32
|
+
entry = userdata.split('&').select { |entry| entry =~ /RS_rn_auth/i }
|
33
|
+
raise "ERROR: user data token not found. " +
|
34
|
+
"Does your MCI have a provides:rs_agent_type=right_link tag?" unless entry
|
35
|
+
token = entry.first.split('=')[1]
|
36
|
+
"#{@url}/servers/data_injection_payload/#{token}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_server_by_name(name)
|
40
|
+
server_list = @connection.servers.index(:filter => ["name==#{name}"])
|
41
|
+
raise "More than one server with the name of '#{name}'. " +
|
42
|
+
"Please fix via the RightScale dashboard and retry." if server_list.size > 1
|
43
|
+
server_list.first
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_deployment_by_name(name)
|
47
|
+
deployment = nil
|
48
|
+
deployments_list = @connection.deployments.index(:filter => ["name==#{name}"])
|
49
|
+
raise "More than one deployment with the name of '#{name}'. " +
|
50
|
+
"Please fix via the RightScale dashboard and retry." if deployments_list.size > 1
|
51
|
+
deployment = deployments_list.first unless deployments_list.empty?
|
52
|
+
deployment
|
53
|
+
end
|
54
|
+
|
55
|
+
def list_clouds
|
56
|
+
@connection.clouds.index
|
57
|
+
end
|
58
|
+
|
59
|
+
# returns:: String if cloud is found, nil if not found
|
60
|
+
def find_cloud_by_name(name)
|
61
|
+
cloud = nil
|
62
|
+
cloud_list = @connection.clouds.index(:filter => ["name==#{name}"])
|
63
|
+
raise "More than one cloud with the name of '#{name}'. " +
|
64
|
+
"Please fix via the RightScale dashboard and retry." if cloud_list.size > 1
|
65
|
+
cloud = cloud_list.first unless cloud_list.empty?
|
66
|
+
cloud
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_mci_by_name(mci_name)
|
70
|
+
mci = nil
|
71
|
+
mci_list = @connection.multi_cloud_images.index(:filter => ["name==#{mci_name}"])
|
72
|
+
raise "More than one MultiCloud image with the name of '#{mci_name}'. " +
|
73
|
+
"Please fix via the RightScale dashboard and retry." if mci_list.size > 1
|
74
|
+
mci = mci_list.first unless mci_list.empty?
|
75
|
+
mci
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_servertemplate(name_or_id)
|
79
|
+
server_template = nil; id = nil; name = nil
|
80
|
+
|
81
|
+
# detect if user passed in a name or an id
|
82
|
+
# there is probably a cleaner way to do this, but I am lazy ATM.
|
83
|
+
begin
|
84
|
+
id = Integer(name_or_id)
|
85
|
+
rescue Exception => e
|
86
|
+
name = name_or_id # Cannot be case to integer, assume a name was passed
|
87
|
+
end
|
88
|
+
|
89
|
+
if name
|
90
|
+
# find ServerTemplate by name
|
91
|
+
st_list = @connection.server_templates.index(:filter => ["name==#{name}"])
|
92
|
+
num_matching_sts = 0
|
93
|
+
st_list.each do |st|
|
94
|
+
if st.name == name
|
95
|
+
server_template = st
|
96
|
+
num_matching_sts += 1
|
97
|
+
end
|
98
|
+
end
|
99
|
+
raise "ERROR: Unable to find ServerTemplate with the name of '#{name}' found " unless server_template
|
100
|
+
raise "ERROR: More than one ServerTemplate with the name of '#{name}' found " +
|
101
|
+
"in account. Please fix via the RightScale dashboard and retry." if num_matching_sts > 1
|
102
|
+
|
103
|
+
else
|
104
|
+
# find ServerTemplate by id
|
105
|
+
server_template = @connection.server_templates.index(:id => id)
|
106
|
+
end
|
107
|
+
|
108
|
+
server_template
|
109
|
+
end
|
110
|
+
|
111
|
+
def create_deployment(name)
|
112
|
+
@connection.deployments.create(:deployment => { :name => name, :decription => "Created by the Vagrant"})
|
113
|
+
end
|
114
|
+
|
115
|
+
def destroy_deployment(deployment)
|
116
|
+
deployment.destroy
|
117
|
+
end
|
118
|
+
|
119
|
+
def create_server(deployment, server_template, mci, cloud, name)
|
120
|
+
# check params
|
121
|
+
unless st_href = server_template.show.href
|
122
|
+
raise "ERROR: ServerTemplate parameter not initialized properly"
|
123
|
+
end
|
124
|
+
|
125
|
+
unless mci.nil?
|
126
|
+
unless mci_href = mci.show.href
|
127
|
+
raise "ERROR: Multi Cloud Image parameter not initialized properly"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
unless d_href = deployment.show.href
|
132
|
+
raise "ERROR: Deployment parameter not initialized properly"
|
133
|
+
end
|
134
|
+
|
135
|
+
unless c_href = cloud.show.href
|
136
|
+
raise "ERROR: Deployment parameter not initialized properly"
|
137
|
+
end
|
138
|
+
|
139
|
+
# create server in deployment using specfied ST
|
140
|
+
create_params = {
|
141
|
+
:server => {
|
142
|
+
:name => name,
|
143
|
+
:decription => "Created by the Vagrant",
|
144
|
+
:deployment_href => d_href,
|
145
|
+
:instance => {
|
146
|
+
:cloud_href => c_href,
|
147
|
+
:server_template_href => st_href
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
# Use the MCI if provided otherwise let the API choose the default MCI
|
152
|
+
# in the ServerTemplate.
|
153
|
+
create_params[:server][:instance][:multi_cloud_image_href] = mci_href unless mci_href.nil?
|
154
|
+
server = @connection.servers.create(create_params)
|
155
|
+
end
|
156
|
+
|
157
|
+
def is_provisioned?(server)
|
158
|
+
server.show.api_methods.include?(:current_instance)
|
159
|
+
end
|
160
|
+
|
161
|
+
# @param(Hash) inputs Hash input name/value pairs i.e. { :name => "text:dummy"}
|
162
|
+
def launch_server(server, inputs = { :name => "text:dummy"})
|
163
|
+
server_name = server.show.name
|
164
|
+
server.launch(inputs) # TODO: parse inputs from Vagrantfile
|
165
|
+
# XXX: need to create a new server object after launch -- why? API bug?
|
166
|
+
find_server_by_name(server_name)
|
167
|
+
end
|
168
|
+
|
169
|
+
def terminate_server(server)
|
170
|
+
server.terminate
|
171
|
+
end
|
172
|
+
|
173
|
+
# Only use this *before* you launch the server
|
174
|
+
def set_server_inputs(server, inputs)
|
175
|
+
server.show.next_instance.show.inputs.multi_update({"inputs" => inputs})
|
176
|
+
end
|
177
|
+
|
178
|
+
def server_wait_for_state(server, target_state, delay = 10)
|
179
|
+
current_state = server_state(server)
|
180
|
+
while current_state != target_state
|
181
|
+
raise "Unexpected sever state: #{current_state}" if is_bad?(current_state)
|
182
|
+
puts "Server #{current_state}. Waiting for instance to be in #{target_state} state..."
|
183
|
+
sleep delay
|
184
|
+
current_state = server_state(server)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def set_bad_states(list_array)
|
189
|
+
@bad_states = list_array
|
190
|
+
end
|
191
|
+
|
192
|
+
def is_bad?(state)
|
193
|
+
@bad_states ||= []
|
194
|
+
@bad_states.select{|s| state =~ /#{s}/}.size > 0
|
195
|
+
end
|
196
|
+
|
197
|
+
def server_ready?(server)
|
198
|
+
server_state(server) == "operational"
|
199
|
+
end
|
200
|
+
|
201
|
+
def server_cloud_name(server)
|
202
|
+
instance = instance_from_server(server)
|
203
|
+
cloud = cloud_from_instance(instance)
|
204
|
+
cloud.show.name
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def server_state(server)
|
210
|
+
instance_from_server(server).show.state
|
211
|
+
end
|
212
|
+
|
213
|
+
def instance_from_server(server)
|
214
|
+
server_data = server.show
|
215
|
+
if is_provisioned?(server)
|
216
|
+
server_data.current_instance
|
217
|
+
else
|
218
|
+
server_data.next_instance
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def cloud_from_instance(instance)
|
223
|
+
instance.show.cloud
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Cary Penniman (<cary@rightscale.com>)
|
3
|
+
# Copyright:: Copyright (c) 2013 RightScale, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
module RightApiProvision
|
20
|
+
class RightScaleError < StandardError
|
21
|
+
end
|
22
|
+
|
23
|
+
class MultipleMatchesFound < RightScaleError
|
24
|
+
def initialize(resource, key, value)
|
25
|
+
key = key.to_s.delete("by_") # remove the 'by_' prefix
|
26
|
+
msg = "More than one #{resource} with the #{key} of '#{value}'. " +
|
27
|
+
"Please resolve via the RightScale dashboard and retry."
|
28
|
+
super msg
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Cary Penniman (<cary@rightscale.com>)
|
3
|
+
# Copyright:: Copyright (c) 2013 RightScale, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
module RightApiProvision
|
20
|
+
|
21
|
+
#
|
22
|
+
# This is the main class to use to create a server on the RightScale platform.
|
23
|
+
# Use the {#provision} method to create and launch
|
24
|
+
# the server.
|
25
|
+
#
|
26
|
+
# The other methods are for checking server state and gathering information
|
27
|
+
# once the server is operational.
|
28
|
+
#
|
29
|
+
class Provisioner
|
30
|
+
|
31
|
+
require "logger"
|
32
|
+
|
33
|
+
RETRY_DELAY = 10 # seconds
|
34
|
+
|
35
|
+
def initialize(email, password, account_id, api_url = nil)
|
36
|
+
require_relative "exception"
|
37
|
+
require_relative "api15"
|
38
|
+
@logger = ::Logger.new(STDOUT)
|
39
|
+
@client = RightApiProvision::API15.new
|
40
|
+
@client.connection(email, password, account_id, api_url)
|
41
|
+
end
|
42
|
+
|
43
|
+
def logger(logger)
|
44
|
+
@logger = logger
|
45
|
+
end
|
46
|
+
|
47
|
+
def connection_url
|
48
|
+
raise "No server provisioned. No connection URL available." unless @server
|
49
|
+
unless @data_request_url
|
50
|
+
user_data = @server.current_instance.show(:view => "full").user_data
|
51
|
+
@data_request_url = @client.data_request_url(user_data)
|
52
|
+
@logger.debug "Data Request URL: #{@data_request_url}"
|
53
|
+
end
|
54
|
+
@data_request_url
|
55
|
+
end
|
56
|
+
|
57
|
+
def wait_for_state(desired_state, timeout_sec = RETRY_DELAY)
|
58
|
+
@client.server_wait_for_state(@server, desired_state, timeout_sec)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Provision a server using RightScale
|
62
|
+
#
|
63
|
+
# @param server_name [String] the name to give the server that will be
|
64
|
+
# created.
|
65
|
+
# @param server_template [String] the name or ID of the ServerTemplate to
|
66
|
+
# create the server from.
|
67
|
+
# @param cloud_name [String] name of cloud to provision on.
|
68
|
+
# @param deployment_name [String] name of deployment to add the server to.
|
69
|
+
# This will be created if it does not exist.
|
70
|
+
# @param server_inputs [Array] An array of {Input} objects.
|
71
|
+
# @param ssh_key_id [String] The resource_uuid of an ssh key from the
|
72
|
+
# RightScale dashboard. Only required on EC2 and Eucalyptus.
|
73
|
+
# @param secgroup_id [Array] An array of security group IDs to place the
|
74
|
+
# server in.
|
75
|
+
#
|
76
|
+
# @raise {RightApiProvisionException} if anything
|
77
|
+
# goes wrong
|
78
|
+
def provision(
|
79
|
+
server_name,
|
80
|
+
server_template,
|
81
|
+
cloud_name,
|
82
|
+
deployment_name,
|
83
|
+
server_inputs,
|
84
|
+
multi_cloud_image_name,
|
85
|
+
ssh_key_id = nil, #TODO: support me
|
86
|
+
secgroup_id = nil, #TODO: support me
|
87
|
+
datacenter = nil) #TODO: support me
|
88
|
+
|
89
|
+
|
90
|
+
# fail if the requested cloud is not registered with RightScale account
|
91
|
+
@cloud = @client.find_cloud_by_name(cloud_name)
|
92
|
+
unless @cloud
|
93
|
+
clouds = @client.list_clouds.inject("") { |str, c| str == "" ? c.name : "#{str}, #{c.name}" }
|
94
|
+
raise RightScaleError, "ERROR: cannot find a cloud named: '#{cloud_name}'. " +
|
95
|
+
"Please check the spelling of the 'cloud_name' parameter in " +
|
96
|
+
"your Vagrant file and verify the cloud is registered with " +
|
97
|
+
"your RightScale account? Supported clouds: #{clouds}"
|
98
|
+
end
|
99
|
+
|
100
|
+
# check for existing deployment and server in RightScale account
|
101
|
+
@deployment = @client.find_deployment_by_name(deployment_name)
|
102
|
+
@logger.info "Deployment '#{deployment_name}' #{@deployment ? "found." : "not found."}"
|
103
|
+
@server = @client.find_server_by_name(server_name) if @deployment
|
104
|
+
@logger.info "Server '#{server_name}' #{@server ? "found." : "not found."}"
|
105
|
+
|
106
|
+
# XXX: fails if the server is not running -- fix me!
|
107
|
+
# if @server
|
108
|
+
# # verify existing server is on the cloud we are requesting, if not fail.
|
109
|
+
# cloud_name ||= Config::VAGRANT_CLOUD_NAME
|
110
|
+
# actual_cloud_name = @client.server_cloud_name(@server)
|
111
|
+
# raise RightScaleError, "ERROR: the server is in the '#{actual_cloud_name}' cloud, " +
|
112
|
+
# "and not in the requested '#{cloud_name}' cloud.\n" +
|
113
|
+
# "Please delete the server or pick and new server name." if cloud_name != actual_cloud_name
|
114
|
+
# end
|
115
|
+
|
116
|
+
unless @deployment && @server
|
117
|
+
# we need to create a server, can we find the servertemplate?
|
118
|
+
begin
|
119
|
+
@servertemplate = @client.find_servertemplate(server_template)
|
120
|
+
rescue
|
121
|
+
raise RightScaleError, "ERROR: cannot find ServerTemplate '#{server_template}'. Did you import it?\n" +
|
122
|
+
"Visit http://bit.ly/VnOiA7 for more info.\n\n"
|
123
|
+
# can we find the MCI?
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
unless @deployment && @server
|
128
|
+
# We need to find the to be used in the server if the MCI name is given
|
129
|
+
begin
|
130
|
+
@mci =
|
131
|
+
if multi_cloud_image_name.nil? || multi_cloud_image_name.empty?
|
132
|
+
nil
|
133
|
+
else
|
134
|
+
@client.find_mci_by_name(multi_cloud_image_name)
|
135
|
+
end
|
136
|
+
rescue Exception => e
|
137
|
+
raise RightScaleError, "ERROR: Cannot find the mci '#{multi_cloud_image_name}'. Please make sure" +
|
138
|
+
" that you have the MCI under the server template selected." +
|
139
|
+
" Exception: #{e.inspect}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# create deployment and server as needed
|
144
|
+
unless @deployment
|
145
|
+
@deployment = @client.create_deployment(deployment_name)
|
146
|
+
@logger.info "Created deployment."
|
147
|
+
end
|
148
|
+
|
149
|
+
unless @server
|
150
|
+
@server = @client.create_server(@deployment, @servertemplate, @mci, @cloud, server_name)
|
151
|
+
@logger.info "Created server."
|
152
|
+
end
|
153
|
+
|
154
|
+
unless @client.is_provisioned?(@server)
|
155
|
+
|
156
|
+
# setup any inputs
|
157
|
+
begin
|
158
|
+
@client.set_server_inputs(@server, server_inputs) if server_inputs && ! server_inputs.empty?
|
159
|
+
rescue Exception => e
|
160
|
+
raise RightScaleError, "Problem setting inputs. \n #{e.message}\n\n"
|
161
|
+
# can we find the MCI?
|
162
|
+
#TODO: @mci = @client.find_multicloudimage_by_name(@servertemplate, multi_cloud_image_name)
|
163
|
+
end
|
164
|
+
|
165
|
+
# launch server
|
166
|
+
@logger.info "Launching server..."
|
167
|
+
@server = @client.launch_server(@server, server_inputs)
|
168
|
+
@client.server_wait_for_state(@server, "booting")
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
# Register a custom progress indicator
|
174
|
+
#
|
175
|
+
# @example
|
176
|
+
# do
|
177
|
+
# @TODO: add an example
|
178
|
+
# end
|
179
|
+
# @param progress_block [Block] block to execute before each loop iteration.
|
180
|
+
# @yield [Logger] A Logger object will be passed as a parameter into
|
181
|
+
# your block.
|
182
|
+
def register_progress_indicator(&progress_block)
|
183
|
+
@progress_indicator = progress_block
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
|
193
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'right_api_provision/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "right_api_provision"
|
8
|
+
gem.version = RightApiProvision::VERSION
|
9
|
+
gem.authors = ["caryp"]
|
10
|
+
gem.email = ["cary@rightscale.com"]
|
11
|
+
gem.description = %q{Simple ruby API for provisioning servers on the cloud with RightScale}
|
12
|
+
gem.summary = %q{Simple ruby API for provisioning servers on the cloud with RightScale}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.add_dependency "right_api_client"
|
20
|
+
%w(rspec-core rspec-expectations rspec-mocks rspec_junit_formatter rake).each { |rspec_gem| gem.add_development_dependency rspec_gem }
|
21
|
+
end
|