right_api_provision 0.1.0
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/.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
|