rightscale-cli 0.5.6 → 0.6.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.
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