rightscale-cli 0.5.6 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d73aea7fe3a92d3746b84c82a0a05b95687f448d
4
- data.tar.gz: 2b30e3d123ffcbbb732c8f156b8dc00ae9094e2a
3
+ metadata.gz: 7ac2dba1341563ceefeaa1e8da61e225799b5ef8
4
+ data.tar.gz: 74e0e2e6a34f4b6de87d4b8a4cdb1de99512c1fe
5
5
  SHA512:
6
- metadata.gz: 7f23569d351f6677090c1586981c12cb20baecc7f8ca25ce8038c03f43017015db4f5b55ae056e3b4f81097fbfd7d1c415224febe84b96363282447dcedc7484
7
- data.tar.gz: 75b64675b468f59833906310c752860bc7ce0d2a82982a02c8422d5f0f6b0045c13dacee1518e43da83d43c99fe10f4eae4d129d8a8edb409c060fdcc884f813
6
+ metadata.gz: bb6ed8e5bd24e54bbcf080fa4c9a49c58cab9f85c3f93724316e1c3ab17a8e647dc9f15784ecd2fb2f557b782fd91d08da8659c3b9ce002ef8634e5863edc0a8
7
+ data.tar.gz: 84822a590b5063a86d9857d0502706adfb2f5cfcfd3adbacd58252dc1b7a8b85ec1a69c96acc80b909a5cf3531def1a9df6aaa5c8ea17fe5d6bfc4743052ca22
data/README.md CHANGED
@@ -51,13 +51,15 @@ This can easily be done interactively with the tool once installed:
51
51
 
52
52
  $ rs configure all
53
53
 
54
+ If your account uses SSO/OAuth, just press enter when being asked for username and password; enter your API refresh token when prompted.
55
+
54
56
  Or, manually setup `~/.rightscale/right_api_client.yml` with your RightScale credentials.
55
57
  Ensure the correct shard for your account is set with `:api_url`.
56
58
  An example file is available, https://github.com/rightscale/right_api_client/blob/master/config/login.yml.example.
57
59
 
58
60
  ## Usage
59
61
 
60
- There is one command, `rs`.
62
+ There is one command, `rs`. For convenience, `rightscale` is also provided.
61
63
 
62
64
  * For a list of commands, type `rs help`
63
65
  * For a list of subcommands, type `rs <namespace> help`, e.g. `rs arrays help`
@@ -69,6 +71,14 @@ List all clouds within your account:
69
71
 
70
72
  $ rs clouds list
71
73
 
74
+ Add a security group to a VPC network in AP-Tokyo for access to the RightScale CMP:
75
+
76
+ $ rs sec-groups create_rs_mgmt --cloud=5 9IPAD59DMD22V
77
+
78
+ Export a deployment to a CAT/RCL file i.e. RightScale Self-Service:
79
+
80
+ $ rs deployments export 491943001
81
+
72
82
  ## License and Authors
73
83
 
74
84
  * Author:: Chris Fordham <chris [at] fordham [hyphon] nagy [dot] id [dot] au>
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # ./rightscale - Run the rightscale-cli client
4
+ #
5
+ # Author:: Chris Fordham (<chris@fordham-nagy.id.au>)
6
+ # Copyright:: Copyright (c) 2013-2105 Chris Fordham
7
+ # License:: Apache License, Version 2.0
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+
21
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
22
+
23
+ require 'rightscale_cli'
24
+
25
+ RightScaleCLI::Base.start
data/bin/rs CHANGED
@@ -3,7 +3,7 @@
3
3
  # ./rs - Run the rightscale-cli client
4
4
  #
5
5
  # Author:: Chris Fordham (<chris@fordham-nagy.id.au>)
6
- # Copyright:: Copyright (c) 2013 Chris Fordham
6
+ # Copyright:: Copyright (c) 2013-2105 Chris Fordham
7
7
  # License:: Apache License, Version 2.0
8
8
  #
9
9
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,8 +18,7 @@
18
18
  # See the License for the specific language governing permissions and
19
19
  # limitations under the License.
20
20
 
21
- require 'rubygems'
22
- $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
21
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
23
22
 
24
23
  require 'rightscale_cli'
25
24
 
@@ -16,8 +16,8 @@
16
16
 
17
17
  def ask_pass
18
18
  require 'io/console'
19
- print 'Password: '
19
+ print 'RightScale Password: '
20
20
  password = STDIN.noecho(&:gets).strip
21
21
  puts
22
- return password
22
+ password
23
23
  end
@@ -0,0 +1,378 @@
1
+
2
+ def server_array_to_cat( sa, rname )
3
+
4
+ puts " ServerArray: " + sa.name
5
+ str = ""
6
+
7
+ # Some of the basic resource information
8
+ str += "resource '"+rname+"', type: 'server_array' do\n"
9
+ str += " name '"+sa.name.gsub(/\'/,"\\\\'")+"'\n"
10
+ if !sa.description.nil?
11
+ str += " description '"+sa.description.gsub(/\'/,"\\\\'")+"'\n"
12
+ end
13
+
14
+ # Get the instance information and the ServerTemplate details
15
+ ni = sa.show.next_instance.show(:view=>"full")
16
+
17
+ str += instance_details_to_cat(ni)
18
+ str += server_template_details_to_cat(ni)
19
+
20
+ if sa.raw["optimized"]
21
+ str += " optimized '#{sa.optimized}'\n"
22
+ end
23
+
24
+ if sa.raw["state"]
25
+ str += " state '"+sa.state+"'\n"
26
+ end
27
+
28
+ if sa.raw["array_type"]
29
+ str += " array_type '"+sa.array_type+"'\n"
30
+ end
31
+
32
+ str += " elasticity_params do {\n"
33
+ str += " 'bounds' => {\n"
34
+ str += " 'min_count' => "+sa.elasticity_params["bounds"]["min_count"]+",\n"
35
+ str += " 'max_count' => "+sa.elasticity_params["bounds"]["max_count"]+"\n"
36
+ str += " },\n"
37
+ str += " 'pacing' => {\n"
38
+ str += " 'resize_calm_time' => "+sa.elasticity_params["pacing"]["resize_calm_time"]+",\n"
39
+ str += " 'resize_down_by' => "+sa.elasticity_params["pacing"]["resize_down_by"]+",\n"
40
+ str += " 'resize_up_by' => "+sa.elasticity_params["pacing"]["resize_up_by"]+"\n"
41
+ str += " },\n"
42
+ str += " 'alert_specific_params' => {\n"
43
+ str += " 'decision_threshold' => "+sa.elasticity_params["alert_specific_params"]["decision_threshold"]+",\n"
44
+ str += " 'voters_tag_predicate' => '"+sa.elasticity_params["alert_specific_params"]["voters_tag_predicate"]+"'\n"
45
+ str += " }\n"
46
+ str += " } end\n"
47
+
48
+ str += "end\n"
49
+ str
50
+ end
51
+
52
+ def server_to_cat( s, rname )
53
+
54
+ puts " Server: " + s.name
55
+
56
+ str = ""
57
+ # Some of the basic resource information
58
+ str += "resource '"+rname+"', type: 'server' do\n"
59
+ str += " name '"+s.name.gsub(/\'/,"\\\\'")+"'\n"
60
+ if !s.description.nil?
61
+ str += " description '"+s.description.gsub(/\'/,"\\\\'")+"'\n"
62
+ end
63
+
64
+ # Get the instance information and the ServerTemplate details
65
+ ni = s.show.next_instance.show(:view=>"full")
66
+
67
+ str += instance_details_to_cat(ni)
68
+ str += server_template_details_to_cat(ni)
69
+
70
+ if s.raw["optimized"]
71
+ str += " optimized '"+s.optimized+"'\n"
72
+ end
73
+
74
+ str += "end\n"
75
+ str
76
+ end
77
+
78
+ def instance_to_cat( i, rname )
79
+
80
+ puts " Instance: " + i.name
81
+
82
+ str = ""
83
+ # Some of the basic resource information
84
+ str += "resource '"+rname+"', type: 'instance' do\n"
85
+ str += " name '"+i.name.gsub(/\'/,"\\\\'")+"'\n"
86
+ if !i.description.nil?
87
+ str += " description '"+i.description.gsub(/\'/,"\\\\'")+"'\n"
88
+ end
89
+
90
+ str += instance_details_to_cat(i)
91
+
92
+ str += "end\n"
93
+ str
94
+ end
95
+
96
+ def server_template_details_to_cat( ni )
97
+
98
+ st = ni.server_template.show(:view=>"inputs_2_0")
99
+
100
+ str = ""
101
+ # Output the server template information
102
+ str += " server_template find('"+st.name.gsub(/\'/,"\\\\'")+"', revision: "+st.revision.to_s()+")\n"
103
+ # str += " server_template_href '"+st.href+"'\n"
104
+
105
+ # For each input, check to see if this input is in the ServerTemplate with the same value
106
+ # If so, skip it, since it appears to be inherited anyway
107
+ inputs = ni.inputs.index(:view=>"inputs_2_0")
108
+ str += " inputs do {\n"
109
+ inputs = inputs.sort_by {|a| a.name.downcase}
110
+ inputs.each do |i|
111
+ if st.raw["inputs"].find{ |sti| sti["name"] == i.name && sti["value"] == i.value }.nil? &&
112
+ ((@deployment_inputs && @dep.raw["inputs"].find{ |sti| sti["name"] == i.name && sti["value"] == i.value }.nil?) || !@deployment_inputs)
113
+ str += " '"+i.name+"' => '"+i.value.gsub(/\'/,"\\\\'")+"',\n" if i.value != "blank"
114
+ end
115
+ end
116
+ str += " } end\n"
117
+
118
+ str
119
+
120
+ end
121
+
122
+ def instance_details_to_cat( ni )
123
+
124
+ str = ""
125
+ str += " cloud '"+ni.cloud.show.name.gsub(/\'/,"\\\\'")+"'\n"
126
+ #str += " cloud_href '"+ni.cloud.show.href+"'\n"
127
+ cloud_href = ni.cloud.show.href
128
+
129
+ # Check to see if there is a datacenter link to export
130
+ if !ni.raw["links"].detect{ |l| l["rel"] == "datacenter" && l["inherited_source"] == nil}.nil?
131
+ begin
132
+ str += " datacenter '"+ni.datacenter.show.name.gsub(/\'/,"\\\\'")+"'\n"
133
+ rescue
134
+ str += " # datacenter ** NOT ABLE TO EXPORT **\n"
135
+ end
136
+ # str += " datacenter_href '"+ni.datacenter.show.href+"'\n"
137
+ end
138
+
139
+ # Check to see if there is a image link to export
140
+ if !ni.raw["links"].detect{ |l| l["rel"] == "image" && l["inherited_source"] == nil}.nil?
141
+ str += " image '"+ni.image.show.name.gsub(/\'/,"\\\\'")+"'\n"
142
+ # str += " image_href '"+ni.image.show.href+"'\n"
143
+ end
144
+
145
+ # Check to see if there is an instance type link to export
146
+ if !ni.raw["links"].detect{ |l| l["rel"] == "instance_type" && l["inherited_source"] == nil}.nil?
147
+ str += " instance_type '"+ni.instance_type.show.name.gsub(/\'/,"\\\\'")+"'\n"
148
+ # str += " instance_type_href '"+ni.instance_type.show.href+"'\n"
149
+ end
150
+
151
+ # Check to see if there is an kernel type link to export
152
+ if !ni.raw["links"].detect{ |l| l["rel"] == "kernel_image" && l["inherited_source"] == nil}.nil?
153
+ str += " kernel_image '"+ni.kernel_image.show.name.gsub(/\'/,"\\\\'")+"'\n"
154
+ # str += " kernel_image_href '"+ni.kernel_image.show.href+"'\n"
155
+ end
156
+
157
+ # Check to see if there is an multi cloud image link to export
158
+ if !ni.raw["links"].detect{ |l| l["rel"] == "multi_cloud_image" && l["inherited_source"] == nil}.nil?
159
+ str += " multi_cloud_image find('"+ni.multi_cloud_image.show.name.gsub(/\'/,"\\\\'")+"', revision: " + ni.multi_cloud_image.show.revision.to_s + ")\n"
160
+ # str += " multi_cloud_image_href '"+ni.multi_cloud_image.show.href+"'\n"
161
+ end
162
+
163
+ # Check to see if there is an ramdisk image link to export
164
+ if !ni.raw["links"].detect{ |l| l["rel"] == "ramdisk_image" && l["inherited_source"] == nil}.nil?
165
+ str += " ramdisk_image '"+ni.ramdisk_image.show.name.gsub(/\'/,"\\\\'")+"'\n"
166
+ # str += " ramdisk_image_href '"+ni.ramdisk_image.show.href+"'\n"
167
+ end
168
+
169
+ # Check to see if there is an ssh key link to export
170
+ if !ni.raw["links"].detect{ |l| l["rel"] == "ssh_key" }.nil?
171
+ begin
172
+ str += " ssh_key '"+ni.ssh_key.show.resource_uid.gsub(/\'/,"\\\\'")+"'\n"
173
+ rescue
174
+ str += " # ssh_key ** NOT ABLE TO EXPORT **\n"
175
+ end
176
+ # str += " ssh_key_href '"+ni.ssh_key.show.href+"'\n"
177
+ end
178
+
179
+ # Export the user_data if it's not blank
180
+ if !ni.user_data.nil? && ni.user_data != ''
181
+ str += " user_data '"+ni.user_data.gsub(/\'/,"\\\\'")+"'\n"
182
+ end
183
+
184
+ # Subnets and security groups aren't proper links in right_api_client, so instead
185
+ # just use the href values for these
186
+ # If we have problems getting any subnet, just ignore them all and print an error
187
+ if !ni.raw["subnets"].nil? && ni.raw["subnets"].size > 0
188
+ begin
189
+ substr = " subnets "
190
+ ni.raw["subnets"].each_with_index do |sn, i|
191
+ snr = @client.resource(sn["href"])
192
+
193
+ # If the name is nil, use the resource_uid and network href
194
+ if !snr.name
195
+ substr += "find(resource_uid: '" + snr.resource_uid + "', network_href: '" + snr.network.href + "')"
196
+ # Not all subnets have networks to check, so if not, just use the name
197
+ elsif !snr.raw["links"].detect{ |l| l["rel"] == "network" }.nil?
198
+ # Check to see if more than one subnet with this name exists in the cloud. If so, use find with the network_href
199
+ if snr.network.show.cloud.show.subnets.index(:filter=>["name==#{snr.name}"]).length == 1
200
+ substr += "'" + snr.name + "'"
201
+ else
202
+ substr += "find('" + snr.name + "', network_href: '" + snr.network.href + "')"
203
+ end
204
+ else
205
+ substr += "'" + snr.name + "'"
206
+ end
207
+
208
+ substr += ", " if i != ni.raw["subnets"].size - 1
209
+ end
210
+ str += substr + "\n"
211
+ rescue
212
+ str += " # subnets ** NOT ABLE TO EXPORT **\n"
213
+ end
214
+ end
215
+
216
+ # Subnets and security groups aren't proper links in right_api_client, so instead
217
+ # just use the href values for these
218
+ if !ni.raw["security_groups"].nil?
219
+ str += " security_groups "
220
+ ni.raw["security_groups"].each_with_index do |sn, i|
221
+ sgr = @client.resource(sn["href"])
222
+
223
+ # Check to see if more than one SG with this name exists in the cloud. If so, use find with the network_href
224
+ if sgr.cloud.show.security_groups.index(:filter=>["name==#{sgr.name}"]).length == 1
225
+ str += "'" + sgr.name + "'"
226
+ else
227
+ str += "find('" + sgr.name + "', network_href: '" + sgr.network.href + "')"
228
+ end
229
+
230
+ str += ", " if i != ni.raw["security_groups"].size - 1
231
+ end
232
+ str += "\n"
233
+ end
234
+
235
+ str
236
+ end
237
+
238
+ def deployment_to_cat_string(client, deployment_id, deployment_inputs, concurrent)
239
+
240
+ # Get and show the deployment name
241
+ @dep = client.deployments(:id=>deployment_id).show(:view=>"inputs_2_0")
242
+ @deployment_inputs = deployment_inputs
243
+ resources = []
244
+
245
+ cat = ''
246
+ cat +=
247
+
248
+ # Output the metadata of this CloudApp
249
+ cat += "name '"+@dep.name.gsub(/\'/,"\\\\'")+"'\n"
250
+ cat += "rs_ca_ver 20131202\n"
251
+
252
+ desc = @dep.description.gsub(/\'/,"\\\\'")
253
+ desc = @dep.name.gsub(/\'/,"\\\\'") if desc == ''
254
+ cat += "short_description '"+desc+"'\n"
255
+
256
+ # For each Server in the deployment (regardless of its status)
257
+ servers = @dep.servers.index
258
+ scount = 0
259
+ servers.each do |s|
260
+ rname = "server_"+(scount+=1).to_s
261
+ cat += server_to_cat(s, rname)
262
+ resources << rname
263
+ end
264
+
265
+ serverarrays = @dep.server_arrays.index
266
+ scount = 0
267
+ serverarrays.each do |sa|
268
+ rname = "server_array_"+(scount+=1).to_s
269
+ cat += server_array_to_cat(sa, rname)
270
+ resources << rname
271
+ end
272
+
273
+ # Iterate through all clouds to get instances in the deployment
274
+ instances = []
275
+ client.clouds.index.each do |c|
276
+ inst = c.instances.index(:filter=>["deployment_href=="+@dep.href],:view=>'full')
277
+ instances += inst
278
+ end
279
+
280
+ # Delete instances with a parent (parent means they're from a Server or ServerArray)
281
+ instances.delete_if { |i| i.raw["links"].detect{ |l| l["rel"] == "parent" } }
282
+ scount = 0
283
+ instances.each do |i|
284
+ rname = "instance_" + (scount+=1).to_s
285
+ cat += instance_to_cat(i, rname)
286
+ resources << rname
287
+ end
288
+
289
+ cat += launch_operation(concurrent, resources) if @deployment_inputs || concurrent
290
+
291
+ cat
292
+ end
293
+
294
+ def launch_operation( concurrent, resources )
295
+ str = ""
296
+
297
+ str += "operation 'launch' do \n"
298
+ str += " description 'Launch the application' \n"
299
+ str += " definition 'generated_launch' \n"
300
+ str += "end \n"
301
+
302
+ rlist = ""
303
+ resources.each_with_index do |r, i|
304
+ rlist += "@" + r
305
+ rlist += ", " if i != resources.size - 1
306
+ end
307
+
308
+ str += "define generated_launch("
309
+ str += rlist if concurrent
310
+ str += ") "
311
+ str += " return #{rlist} " if concurrent
312
+ str += " do \n"
313
+
314
+ str += deployment_inputs_to_cat if @deployment_inputs
315
+ str += concurrent_resource_launch(resources) if concurrent
316
+
317
+ str += "end \n"
318
+ str
319
+
320
+ end
321
+
322
+ def concurrent_resource_launch(resources)
323
+ puts "Creating concurrent launch code"
324
+
325
+ str = ""
326
+
327
+ resources.each do |r|
328
+ str += " @@global_" + r + " = @" + r + "\n"
329
+ end
330
+
331
+ str += " concurrent do \n"
332
+ resources.each do |r|
333
+ str += " provision(@@global_" + r + ")\n"
334
+ end
335
+ str += " end \n"
336
+
337
+ resources.each do |r|
338
+ str += " @" + r + " = @@global_" + r + "\n"
339
+ end
340
+
341
+ str
342
+ end
343
+
344
+ def deployment_inputs_to_cat()
345
+ puts "Creating deployment-level inputs"
346
+
347
+ str = "\n\n"
348
+
349
+ inputs = @dep.raw["inputs"].select{ |i| i["value"] != "blank"}
350
+ if inputs.size > 0
351
+ str += " $inp = {\n"
352
+ inputs.each_with_index do |input, i|
353
+ str += " '" + input["name"] + "':'" + input["value"] + "'"
354
+ str += "," if i != inputs.size - 1
355
+ str += "\n"
356
+ end
357
+ str += " } \n"
358
+ str += " @@deployment.multi_update_inputs(inputs: $inp) \n"
359
+ else
360
+ str += " # No deployment level inputs found \n"
361
+ end
362
+ str
363
+ end
364
+
365
+ def deployment_to_cat_file( client, deployment_id, deployment_inputs, concurrent )
366
+
367
+ # Get and show the deployment name
368
+ dep = client.deployments(:id=>deployment_id).show
369
+ puts "Exporting Deployment: " + dep.name
370
+
371
+ # Output to a file named after the deployment (cleaned up for Linux filenames)
372
+ File.open(dep.name.gsub(/[^\w\s_-]+/, '')+'.cat.rb','w') do |f|
373
+
374
+ f.puts deployment_to_cat_string( client, deployment_id, deployment_inputs, concurrent)
375
+
376
+ end
377
+
378
+ end