rackspace_cloudservers_api 0.0.2

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 ADDED
@@ -0,0 +1,2 @@
1
+ nbproject
2
+ pkg
data/History.txt ADDED
@@ -0,0 +1 @@
1
+ == 0.0.0 2009-07-02
data/Manifest.txt ADDED
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/benchmark_fix.rb
6
+ lib/right_rackspace.rb
7
+ lib/rackspace_base.rb
8
+ lib/rackspace.rb
9
+ lib/support.rb
10
+ test/_test_credentials.rb
11
+ test/test_right_rackspace.rb
data/README.txt ADDED
@@ -0,0 +1,47 @@
1
+ == Rackspace CloudServers API gem
2
+
3
+ Currently this is just a fork of the rightscale gem so that I can make any required customisations myself. I haven't been able to run the tests yet, so don't use it yet!
4
+
5
+ - Daniel
6
+
7
+ = RightScale Rackspace Ruby Gems
8
+
9
+ Published by RightScale, Inc. under the MIT License.
10
+ For information about RightScale, see http://www.rightscale.com
11
+
12
+ == DESCRIPTION:
13
+
14
+ == FEATURES:
15
+
16
+ - Full programmmatic access to Rackspace.
17
+
18
+ == REQUIREMENTS:
19
+
20
+ == INSTALL:
21
+
22
+ sudo gem install right_rackspace
23
+
24
+ == LICENSE:
25
+
26
+ Copyright (c) 2007-2008 RightScale, Inc.
27
+
28
+ Permission is hereby granted, free of charge, to any person obtaining
29
+ a copy of this software and associated documentation files (the
30
+ 'Software'), to deal in the Software without restriction, including
31
+ without limitation the rights to use, copy, modify, merge, publish,
32
+ distribute, sublicense, and/or sell copies of the Software, and to
33
+ permit persons to whom the Software is furnished to do so, subject to
34
+ the following conditions:
35
+
36
+ The above copyright notice and this permission notice shall be
37
+ included in all copies or substantial portions of the Software.
38
+
39
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
40
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
42
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
43
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
44
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
45
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46
+
47
+
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require "rake/testtask"
6
+ require 'rcov/rcovtask'
7
+ $: << File.dirname(__FILE__)
8
+ require 'lib/right_rackspace.rb'
9
+
10
+ ##testglobs = ["test/ts_right_aws.rb"]
11
+
12
+
13
+ # Suppress Hoe's self-inclusion as a dependency for our Gem. This also keeps
14
+ # Rake & rubyforge out of the dependency list. Users must manually install
15
+ # these gems to run tests, etc.
16
+ class Hoe
17
+ def extra_deps
18
+ @extra_deps.reject do |x|
19
+ Array(x).first == 'hoe'
20
+ end
21
+ end
22
+ end
23
+
24
+ Hoe.new('right_rackspace', RightRackspace::VERSION::STRING) do |p|
25
+ p.rubyforge_name = 'rightrackspace'
26
+ p.author = 'RightScale, Inc.'
27
+ p.email = 'support@rightscale.com'
28
+ p.summary = 'Interface classes for the Rackspace Services'
29
+ ## p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
30
+ ## p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
31
+ ## p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
32
+ ## p.remote_rdoc_dir = "/right_rackspace_gem_doc"
33
+ p.extra_deps = [['right_http_connection','>= 1.2.1']]
34
+ ## p.test_globs = testglobs
35
+ end
36
+
37
+ ##desc "Analyze code coverage of the unit tests."
38
+ ##Rcov::RcovTask.new do |t|
39
+ ## t.test_files = FileList[testglobs]
40
+ ## #t.verbose = true # uncomment to see the executed command
41
+ ##end
42
+
43
+ # vim: syntax=Ruby
44
+
45
+
46
+ begin
47
+ require 'jeweler'
48
+ Jeweler::Tasks.new do |gemspec|
49
+ gemspec.name = "rackspace_cloudservers_api"
50
+ gemspec.summary = "CloudServers Api based on RightScale's gem"
51
+ gemspec.description = "CloudServers Api based on RightScale's gem"
52
+ gemspec.email = "daniel.gemcutter@tenner.org"
53
+ gemspec.homepage = "http://github.com/swombat/rackspace_cloudservers_api"
54
+ gemspec.authors = ["Daniel Tenner", "Rightscale"]
55
+ end
56
+ rescue LoadError
57
+ puts "Jeweler not available. Install it with: gem install jeweler"
58
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
@@ -0,0 +1,39 @@
1
+ #
2
+ # Copyright (c) 2007-2009 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ #
24
+
25
+
26
+ # A hack because there's a bug in add! in Benchmark::Tms
27
+ module Benchmark #:nodoc:
28
+ class Tms #:nodoc:
29
+ def add!(&blk)
30
+ t = Benchmark::measure(&blk)
31
+ @utime = utime + t.utime
32
+ @stime = stime + t.stime
33
+ @cutime = cutime + t.cutime
34
+ @cstime = cstime + t.cstime
35
+ @real = real + t.real
36
+ self
37
+ end
38
+ end
39
+ end
data/lib/rackspace.rb ADDED
@@ -0,0 +1,743 @@
1
+ #
2
+ # Copyright (c) 2009 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+ #
24
+ module Rightscale
25
+ module Rackspace
26
+
27
+ # TODO: KD: Enable this feature when Packspace get rid of the caching issue
28
+ PAGINATION_ENABLED = false
29
+
30
+ # == Rightscale::Rackspace::Interface
31
+ #
32
+ # === Examples:
33
+ #
34
+ # # Create a handle
35
+ # rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d', :verbose_errors => true )
36
+ #
37
+ # # list images and flavors
38
+ # rackspace.list_images(:detaile => true)
39
+ # rackspace.list_flavors(:detaile => true)
40
+ #
41
+ # # launch a new server
42
+ # image_id = 8
43
+ # flavor_id = 4
44
+ # new_server = rackspace.create_server('my-awesome-server', image_id, flavor_id,
45
+ # :metadata => {
46
+ # 'metakey1' => 'metavalue1',
47
+ # 'metakey2' => 'metavalue2',
48
+ # 'metakey3' => 'metavalue3' },
49
+ # :personalities => {
50
+ # '/home/file1.txt' => 'ohoho!',
51
+ # '/home/file2.txt' => 'ahaha!',
52
+ # '/home/file3.rb' => 'puts "Olalah!"'}
53
+ #
54
+ # puts "New server password is: #{new_server['server']['adminPass']}"
55
+ #
56
+ # # change the server password
57
+ # rackspace.update_server(new_server['server']['id'], 'my_new_password')
58
+ #
59
+ # # "upgrade" the server
60
+ # new_flavor_id = 6
61
+ # rackspace.resize_server(new_server['server']['id'], new_flavor_id)
62
+ # rackspace.confirm_resized_server(new_server['server']['id'])
63
+ #
64
+ # # make an image from the server
65
+ # new_image = rackspace.create_image(new_server['server']['id'], 'my-awesome-image-0001')
66
+ #
67
+ # # make a hard reboot (power off)
68
+ # rackspace.reboot_server(new_server['server']['id'], :hard)
69
+ #
70
+ # # delete the server
71
+ # rackspace.delete_server(new_server['server']['id'])
72
+ #
73
+ # === RightRackspace gem caching usage (only list_xxx calls). This caching does not hit if any additional URL variables are set through :vars hash.
74
+ #
75
+ # # Create a handle
76
+ # rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d', :caching => true)
77
+ #
78
+ # # make a call. this fills the internal cache with the response and
79
+ # # a current timestamp
80
+ # old_list = rackspace.list_servers(:detail => true)
81
+ #
82
+ # # sleep a bit
83
+ # sleep 5
84
+ #
85
+ # # make another call
86
+ # begin
87
+ # new_list = rackspace.list_servers(:detail => true)
88
+ # rescue Rightscale::Rackspace::NoChange
89
+ # puts e.message #=> "Cached: '/servers/detail' has not changed since Thu, 09 Jul 2009 10:53:35 GMT."
90
+ # # extract the list of servers from internal cache
91
+ # new_list = rackspace.cache['/servers/detail'][:data]
92
+ # # the lists must be equal
93
+ # puts old_list == new_list
94
+ # end
95
+ #
96
+ # # non detailed list of servers
97
+ # rackspace.list_servers
98
+ # begin
99
+ # flavors = rackspace.list_servers
100
+ # rescue Rightscale::Rackspace::NoChange
101
+ # flavors = rackspace.cache['/servers'][:data]
102
+ # ens
103
+ #
104
+ # # this does not hit internal cache doe to the :vars.
105
+ # new_list = rackspace.list_servers(:detail => true, :vars => {'format' => 'json'})
106
+ #
107
+ # === Rackspace service caching usage:
108
+ #
109
+ # # Create a handle
110
+ # rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d')
111
+ #
112
+ # # 'if-modified-since' HTTP request header usage:
113
+ # last_request_time = Time.now - 3600
114
+ # begin
115
+ # rackspace.list_servers(:detail => true, :headers => { 'if-modified-since' => last_request_time })
116
+ # rescue Rightscale::Rackspace::NoChange => e
117
+ # # e.message can return one of the messages below:
118
+ # # - "Cached: '/servers/detail' has not changed since Thu, 09 Jul 2009 10:55:41 GMT."
119
+ # # - "NotModified: '/servers/detail' has not changed since Thu, 09 Jul 2009 10:55:41 GMT."
120
+ # # The first comes when we have cached response from Rackspace and it can be retreived as
121
+ # # rackspace.cache['/servers/detail'][:data]. The second one appears when the local
122
+ # # cache has no stored records for the request.
123
+ # puts e.message
124
+ # end
125
+ #
126
+ # # 'changes-since' URL variable usage:
127
+ # begin
128
+ # new_servers = rackspace.list_servers(:detail => true, :vars => { 'changes-since' => last_request_time })
129
+ # # show the changes at servers since last_request_time
130
+ # puts new_servers.inspect
131
+ # rescue Rightscale::Rackspace::NoChange => e
132
+ # puts e.message #=>
133
+ # "NotModified: '/flavors?changes-since=1247137435&limit=1000&offset=0' has not changed since the requested time."
134
+ # end
135
+ #
136
+ # === Callbacks:
137
+ #
138
+ # # On response calback
139
+ # on_response = Proc.new do |handle|
140
+ # puts ">> response headers: #{handle.last_response.to_hash.inspect}"
141
+ # end
142
+ #
143
+ # # On error calback
144
+ # on_error = Proc.new do |handle, error_message|
145
+ # puts ">> Error: #{error_message}"
146
+ # end
147
+ #
148
+ # # Create a handle
149
+ # rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d',
150
+ # :on_response => on_response,
151
+ # :on_error => on_error)
152
+ #
153
+ class Interface
154
+
155
+ # The login is executed automatically when one calls any othe API call.
156
+ # The only use case for this method is when one need to pass any custom
157
+ # headers or URL vars during a login process.
158
+ #
159
+ # rackspace.login #=> true
160
+ #
161
+ def login(opts={})
162
+ authenticate(opts)
163
+ end
164
+
165
+ # List all API versions supported by a Service Endpoint.
166
+ #
167
+ # rackspace.list_api_versions #=> {"versions"=>[{"id"=>"v1.0", "status"=>"BETA"}]}
168
+ #
169
+ # RightRackspace caching: yes, key: '/'
170
+ #
171
+ def list_api_versions(opts={})
172
+ api_or_cache(:get, "/", opts.merge(:no_service_path => true))
173
+ end
174
+
175
+ # Determine rate limits.
176
+ #
177
+ # rackspace.list_limits #=>
178
+ # {"limits"=>
179
+ # {"absolute"=>
180
+ # {"maxNumServers"=>25, "maxIPGroups"=>50, "maxIPGroupMembers"=>25},
181
+ # "rate"=>
182
+ # [{"regex"=>".*",
183
+ # "verb"=>"PUT",
184
+ # "URI"=>"*",
185
+ # "remaining"=>10,
186
+ # "unit"=>"MINUTE",
187
+ # "value"=>10,
188
+ # "resetTime"=>1246604596},
189
+ # {"regex"=>"^/servers",
190
+ # "verb"=>"POST",
191
+ # "URI"=>"/servers*",
192
+ # "remaining"=>1000,
193
+ # "unit"=>"DAY",
194
+ # "value"=>1000,
195
+ # "resetTime"=>1246604596}, ...]}}
196
+ #
197
+ #
198
+ def list_limits(opts={})
199
+ # # RightRackspace caching: yes, key: '/limits'
200
+ # api_or_cache(:get, "/limits", opts)
201
+ api(:get, "/limits", opts)
202
+ end
203
+
204
+ #--------------------------------
205
+ # Images
206
+ #--------------------------------
207
+
208
+ # List images. Options: :detail => false|true.
209
+ #
210
+ # # Get images list.
211
+ # rackspace.list_images #=>
212
+ # {"images"=>
213
+ # [{"name"=>"CentOS 5.2", "id"=>2},
214
+ # {"name"=>"Gentoo 2008.0", "id"=>3},
215
+ # {"name"=>"Debian 5.0 (lenny)", "id"=>4},
216
+ # ...}]}
217
+ #
218
+ # # Get the detailed images description.
219
+ # rackspace.list_images(:detail => true) #=>
220
+ # {"images"=>
221
+ # [{"name"=>"CentOS 5.2", "id"=>2, "status"=>"ACTIVE"},
222
+ # {"name"=>"Gentoo 2008.0",
223
+ # "id"=>3,
224
+ # "updated"=>"2007-10-24T12:52:03-05:00",
225
+ # "status"=>"ACTIVE"},
226
+ # {"name"=>"Debian 5.0 (lenny)", "id"=>4, "status"=>"ACTIVE"},
227
+ # {"name"=>"Fedora 10 (Cambridge)", "id"=>5, "status"=>"ACTIVE"},
228
+ # {"name"=>"CentOS 5.3", "id"=>7, "status"=>"ACTIVE"},
229
+ # {"name"=>"Ubuntu 9.04 (jaunty)", "id"=>8, "status"=>"ACTIVE"},
230
+ # {"name"=>"Arch 2009.02", "id"=>9, "status"=>"ACTIVE"},
231
+ # {"name"=>"Ubuntu 8.04.2 LTS (hardy)", "id"=>10, "status"=>"ACTIVE"},
232
+ # {"name"=>"Ubuntu 8.10 (intrepid)", "id"=>11, "status"=>"ACTIVE"},
233
+ # {"name"=>"Red Hat EL 5.3", "id"=>12, "status"=>"ACTIVE"},
234
+ # {"name"=>"Fedora 11 (Leonidas)", "id"=>13, "status"=>"ACTIVE"},
235
+ # {"name"=>"my-awesome-image-0001",
236
+ # "serverId"=>62844,
237
+ # "progress"=>100,
238
+ # "id"=>3226,
239
+ # "updated"=>"2009-07-07T01:20:48-05:00",
240
+ # "status"=>"ACTIVE",
241
+ # "created"=>"2009-07-07T01:17:16-05:00"}]}
242
+ #
243
+ # RightRackspace caching: yes, keys: '/images', '/images/detail'
244
+ #
245
+ def list_images(opts={})
246
+ api_or_cache(:get, detailed_path("/images", opts), opts.merge(:incrementally => PAGINATION_ENABLED))
247
+ end
248
+
249
+ # Incrementally list images.
250
+ #
251
+ # # list images by 3
252
+ # rackspace.incrementally_list_images(0, 3, :detail=>true) do |response|
253
+ # puts response.inspect
254
+ # true
255
+ # end
256
+ #
257
+ def incrementally_list_images(offset=nil, limit=nil, opts={}, &block)
258
+ incrementally_list_resources(:get, detailed_path("/images", opts), offset, limit, opts, &block)
259
+ end
260
+
261
+ # Get image data.
262
+ #
263
+ # rackspace.get_image(5) #=>
264
+ # {"image"=>{"name"=>"Fedora 10 (Cambridge)", "id"=>5, "status"=>"ACTIVE"}}
265
+ #
266
+ def get_image(image_id, opts={})
267
+ api(:get, "/images/#{image_id}", opts)
268
+ end
269
+
270
+ # Create a new image for the given server ID. Once complete, a new image will be
271
+ # available that can be used to rebuild or create servers. Specifying the same image name as an
272
+ # existing custom image replaces the image
273
+ #
274
+ # # create an image
275
+ # new_image = rackspace.create_image(62844, 'my-awesome-image-0001') #=>
276
+ # {"image"=>{"name"=>"my-awesome-image-0001", "serverId"=>62844, "id"=>3226}}
277
+ #
278
+ # # sleep a bit
279
+ # sleep 10
280
+ #
281
+ # # get the new image status
282
+ # rackspace.get_image(new_image['image']['id']) #=>
283
+ # {"image"=>
284
+ # {"name"=>"my-awesome-image-0001",
285
+ # "progress"=>78,
286
+ # "id"=>3226,
287
+ # "updated"=>"2009-07-07T01:20:16-05:00",
288
+ # "status"=>"SAVING",
289
+ # "created"=>"2009-07-07T01:16:51-05:00"}}
290
+ #
291
+ # # sleep more to make the new image active
292
+ # sleep 60
293
+ #
294
+ # # list all the images
295
+ # rackspace.list_images(:detail => true) #=>
296
+ # {"images"=>
297
+ # [{"name"=>"CentOS 5.2", "id"=>2, "status"=>"ACTIVE"},
298
+ # {"name"=>"Gentoo 2008.0",
299
+ # "id"=>3,
300
+ # "updated"=>"2007-10-24T12:52:03-05:00",
301
+ # "status"=>"ACTIVE"},
302
+ # {"name"=>"Debian 5.0 (lenny)", "id"=>4, "status"=>"ACTIVE"},
303
+ # {"name"=>"Fedora 10 (Cambridge)", "id"=>5, "status"=>"ACTIVE"},
304
+ # {"name"=>"CentOS 5.3", "id"=>7, "status"=>"ACTIVE"},
305
+ # {"name"=>"Ubuntu 9.04 (jaunty)", "id"=>8, "status"=>"ACTIVE"},
306
+ # {"name"=>"Arch 2009.02", "id"=>9, "status"=>"ACTIVE"},
307
+ # {"name"=>"Ubuntu 8.04.2 LTS (hardy)", "id"=>10, "status"=>"ACTIVE"},
308
+ # {"name"=>"Ubuntu 8.10 (intrepid)", "id"=>11, "status"=>"ACTIVE"},
309
+ # {"name"=>"Red Hat EL 5.3", "id"=>12, "status"=>"ACTIVE"},
310
+ # {"name"=>"Fedora 11 (Leonidas)", "id"=>13, "status"=>"ACTIVE"},
311
+ # {"name"=>"my-awesome-image-0001",
312
+ # "serverId"=>62844,
313
+ # "progress"=>100,
314
+ # "id"=>3226,
315
+ # "updated"=>"2009-07-07T01:20:48-05:00",
316
+ # "status"=>"ACTIVE",
317
+ # "created"=>"2009-07-07T01:17:16-05:00"}]}
318
+ #
319
+ #
320
+ def create_image(server_id, name, opts={})
321
+ body = { 'image' => { 'name' => name,
322
+ 'serverId' => server_id } }
323
+ api(:post, "/images", opts.merge(:body => body.to_json))
324
+ end
325
+
326
+ #--------------------------------
327
+ # Flavors
328
+ #--------------------------------
329
+
330
+ # List flavors. Options: :detail => false|true.
331
+ #
332
+ # # Get list of flavors.
333
+ # rackspace.list_flavors #=>
334
+ # {"flavors"=>
335
+ # [{"name"=>"256 slice", "id"=>1},
336
+ # {"name"=>"512 slice", "id"=>2},
337
+ # {"name"=>"1GB slice", "id"=>3},
338
+ # ...}]}
339
+ #
340
+ # # Get the detailed flavors description.
341
+ # rackspace.list_flavors(:detail => true) #=>
342
+ # {"flavors"=>
343
+ # [{"name"=>"256 slice", "id"=>1, "ram"=>256, "disk"=>10},
344
+ # {"name"=>"512 slice", "id"=>2, "ram"=>512, "disk"=>20},
345
+ # {"name"=>"1GB slice", "id"=>3, "ram"=>1024, "disk"=>40},
346
+ # ...}]}
347
+ #
348
+ # # Get the most recent changes or Rightscale::Rackspace::NoChange.
349
+ # # (no RightRackspace gem caching)
350
+ # rackspace.list_flavors(:detail => true, :vars => {'changes-since'=>Time.now-3600}) #=>
351
+ #
352
+ # RightRackspace caching: yes, keys: '/flavors', '/flavors/detail'
353
+ #
354
+ def list_flavors(opts={})
355
+ api_or_cache(:get, detailed_path("/flavors", opts), opts.merge(:incrementally => PAGINATION_ENABLED))
356
+ end
357
+
358
+ # Incrementally list flavors.
359
+ #
360
+ # rackspace.incrementally_list_flavors(0,3) do |response|
361
+ # puts response.inspect
362
+ # true
363
+ # end
364
+ #
365
+ def incrementally_list_flavors(offset=nil, limit=nil, opts={}, &block)
366
+ incrementally_list_resources(:get, detailed_path("/flavors", opts), offset, limit, opts, &block)
367
+ end
368
+
369
+ # Get flavor data.
370
+ #
371
+ # rackspace.get_flavor(5) #=>
372
+ # {"flavor"=>{"name"=>"4GB slice", "id"=>5, "ram"=>4096, "disk"=>160}}
373
+ #
374
+ def get_flavor(flavor_id, opts={})
375
+ api(:get, "/flavors/#{flavor_id}", opts)
376
+ end
377
+
378
+ #--------------------------------
379
+ # Servers
380
+ #--------------------------------
381
+
382
+ # List servers. Options: :detail => false|true.
383
+ #
384
+ # rackspace.list_servers #=>
385
+ # {"servers"=>[{"name"=>"my-super-awesome-server-", "id"=>62844}]}
386
+ #
387
+ # rackspace.list_servers(:detail => true) #=>
388
+ # {"servers"=>
389
+ # [{"name"=>"my-super-awesome-server-",
390
+ # "addresses"=>
391
+ # {"public"=>["174.143.246.228"], "private"=>["10.176.134.157"]},
392
+ # "progress"=>100,
393
+ # "imageId"=>8,
394
+ # "metadata"=>{"data1"=>"Ohoho!", "data2"=>"Ehehe!"},
395
+ # "id"=>62844,
396
+ # "flavorId"=>3,
397
+ # "hostId"=>"fabfc1cebef6f1d7e4b075138dbd6b46",
398
+ # "status"=>"ACTIVE"}]
399
+ #
400
+ def list_servers(opts={})
401
+ api_or_cache(:get, detailed_path("/servers", opts), opts.merge(:incrementally => PAGINATION_ENABLED))
402
+ end
403
+
404
+ # Incrementally list servers.
405
+ #
406
+ # # list servers by 3
407
+ # rackspace.incrementally_list_servers(0, 3) do |response|
408
+ # puts response.inspect
409
+ # true
410
+ # end
411
+ #
412
+ def incrementally_list_servers(offset=nil, limit=nil, opts={}, &block)
413
+ incrementally_list_resources(:get, detailed_path("/servers", opts), offset, limit, opts, &block)
414
+ end
415
+
416
+ # Launch a new server.
417
+ # +Server_data+ is a hash of params params:
418
+ # Mandatory: :name, :image_id, :flavor_id
419
+ # Optional: :metadata, :personalities
420
+ #
421
+ # rackspace.create_server(
422
+ # :name => 'my-awesome-server',
423
+ # :image_id => 8,
424
+ # :flavor_id => 4,
425
+ # :metadata => { 'KD1' => 'XXXX1', 'KD2' => 'XXXX2'},
426
+ # :personalities => { '/home/1.txt' => 'woo-hoo',
427
+ # '/home/2.rb' => 'puts"Olalah!' }) #=>
428
+ # {"server"=>
429
+ # {"name"=>"my-awesome-server",
430
+ # "addresses"=>{"public"=>["174.143.56.6"], "private"=>["10.176.1.235"]},
431
+ # "progress"=>0,
432
+ # "imageId"=>8,
433
+ # "metadata"=>{"KD1"=>"XXXX1", "KD2"=>"XXXX2"},
434
+ # "adminPass"=>"my-awesome-server85lzHZ",
435
+ # "id"=>2290,
436
+ # "flavorId"=>4,
437
+ # "hostId"=>"19956ee1c79a57e481b652ddf818a569",
438
+ # "status"=>"BUILD"}}
439
+ #
440
+ def create_server(server_data, opts={} )
441
+ personality = server_data[:personalities].to_a.dup
442
+ personality.map! { |file, contents| { 'path'=> file, 'contents' => Base64.encode64(contents).chomp } }
443
+ body = {
444
+ 'server' => {
445
+ 'name' => server_data[:name],
446
+ 'imageId' => server_data[:image_id],
447
+ 'flavorId' => server_data[:flavor_id],
448
+ }
449
+ }
450
+ #body['server']['adminPass'] = server_data[:password] if server_data[:password]
451
+ body['server']['sharedIpGroupId'] = server_data[:shared_ip_group_id] if server_data[:shared_ip_group_id]
452
+ body['server']['metadata'] = server_data[:metadata] unless server_data[:metadata].blank?
453
+ body['server']['personality'] = personality unless personality.blank?
454
+ api(:post, "/servers", opts.merge(:body => body.to_json))
455
+ end
456
+
457
+ # Get a server data.
458
+ # rackspace.get_server(2290)
459
+ # {"server"=>
460
+ # {"name"=>"my-awesome-server",
461
+ # "addresses"=>{"public"=>["174.143.56.6"], "private"=>["10.176.1.235"]},
462
+ # "progress"=>100,
463
+ # "imageId"=>8,
464
+ # "metadata"=>{"KD1"=>"XXXX1", "KD2"=>"XXXX2"},
465
+ # "id"=>2290,
466
+ # "flavorId"=>4,
467
+ # "hostId"=>"19956ee1c79a57e481b652ddf818a569",
468
+ # "status"=>"ACTIVE"}}
469
+ #
470
+ def get_server(server_id, opts={})
471
+ api(:get, "/servers/#{server_id}", opts)
472
+ end
473
+
474
+ # Change server name and/or password.
475
+ # +Server_data+: :name, :password
476
+ #
477
+ # rackspace.update_server(2290, :password => '12345' ) #=> true
478
+ # rackspace.update_server(2290, :name => 'my-super-awesome-server', :password => '67890' ) #=> true
479
+ #
480
+ # P.S. the changes will appers in some seconds.
481
+ #
482
+ # P.P.S. changes server status: 'ACTIVE' -> 'PASSWORD'.
483
+ #
484
+ def update_server(server_id, server_data, opts={})
485
+ body = { 'server' => {} }
486
+ body['server']['name'] = server_data[:name] if server_data[:name]
487
+ body['server']['adminPass'] = server_data[:password] if server_data[:password]
488
+ api(:put, "/servers/#{server_id}", opts.merge(:body => body.to_json))
489
+ end
490
+
491
+ # Reboot a server.
492
+ #
493
+ # # Soft reboot
494
+ # rackspace.reboot_server(2290) #=> true
495
+ #
496
+ # # Hard reboot (power off)
497
+ # rackspace.reboot_server(2290, :hard) #=> true
498
+ #
499
+ def reboot_server(server_id, type = :soft, opts={})
500
+ body = { 'reboot' => { 'type' => type.to_s.upcase } }
501
+ api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
502
+ end
503
+
504
+ # The rebuild function removes all data on the server and replaces it with the specified image.
505
+ # Server id and IP addresses will remain the same.
506
+ #
507
+ # # rebuild a server
508
+ # rackspace.rebuild_server(62844, 3226) #=> true
509
+ #
510
+ # # watch for the progress
511
+ # rackspace.get_server(62844) #=>
512
+ # {"server"=>
513
+ # {"name"=>"my-super-awesome-server-",
514
+ # "addresses"=>{"public"=>["174.143.246.228"], "private"=>["10.176.134.157"]},
515
+ # "progress"=>65,
516
+ # "imageId"=>3226,
517
+ # "metadata"=>{"data1"=>"Ohoho!", "data2"=>"Ehehe!"},
518
+ # "id"=>62844,
519
+ # "flavorId"=>3,
520
+ # "hostId"=>"fabfc1cebef6f1d7e4b075138dbd6b46",
521
+ # "status"=>"REBUILD"}}
522
+ #
523
+ def rebuild_server(server_id, image_id, opts={})
524
+ body = { 'rebuild' => { 'imageId' => image_id } }
525
+ api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
526
+ end
527
+
528
+ # The resize function converts an existing server to a different flavor, in essence, scaling the server up
529
+ # or down. The original server is saved for a period of time to allow rollback if there is a problem. All
530
+ # resizes should be tested and explicitly confirmed, at which time the original server is removed. All
531
+ # resizes are automatically confirmed after 24 hours if they are not explicitly confirmed or reverted.
532
+ #
533
+ # rackspace.resize_server(2290, 3) #=> true
534
+ # rackspace.get_server(2290) #=>
535
+ # {"server"=>
536
+ # {"name"=>"my-awesome-server",
537
+ # "addresses"=>{"public"=>["174.143.56.6"], "private"=>["10.176.1.235"]},
538
+ # "progress"=>0,
539
+ # "imageId"=>8,
540
+ # "metadata"=>{"KD1"=>"XXXX1", "KD2"=>"XXXX2"},
541
+ # "id"=>2290,
542
+ # "flavorId"=>4,
543
+ # "hostId"=>"19956ee1c79a57e481b652ddf818a569",
544
+ # "status"=>"QUEUE_RESIZE"}}
545
+ #
546
+ def resize_server(server_id, flavor_id, opts={})
547
+ body = { 'resize' => { 'flavorId' => flavor_id } }
548
+ api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
549
+ end
550
+
551
+ # Confirm a server resize action.
552
+ #
553
+ # rackspace.confirm_resized_server(2290) #=> true
554
+ #
555
+ def confirm_resized_server(server_id, opts={})
556
+ body = { 'confirmResize' => nil }
557
+ api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
558
+ end
559
+
560
+ # Revert a server resize action.
561
+ #
562
+ # rackspace.revert_resized_server(2290) #=> true
563
+ #
564
+ def revert_resized_server(server_id, opts={})
565
+ body = { 'revertResize' => nil }
566
+ api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
567
+ end
568
+
569
+ #--------------------------------
570
+ # Server addresses
571
+ #--------------------------------
572
+
573
+ # Get server addresses.
574
+ #
575
+ # # get all addresses
576
+ # rackspace.list_addresses(62844) #=>
577
+ # {"addresses"=>{"public"=>["174.143.246.228"], "private"=>["10.176.134.157"]}}
578
+ #
579
+ # # get public addresses
580
+ # rackspace.list_addresses(62844, :public) #=>
581
+ # {"public"=>["174.143.246.228"]}
582
+ #
583
+ # # get private addresses
584
+ # rackspace.list_addresses(62844, :private) #=>
585
+ # {"private"=>["10.176.134.157"]}
586
+ #
587
+ # RightRackspace caching: no
588
+ def list_addresses(server_id, address_type=:all, opts={})
589
+ path = "/servers/#{server_id}/ips"
590
+ case address_type.to_s
591
+ when 'public' then path += "/public"
592
+ when 'private' then path += "/private"
593
+ end
594
+ api(:get, path, opts.merge(:incrementally => PAGINATION_ENABLED))
595
+ end
596
+
597
+ # Share an IP from an existing server in the specified shared IP group to another
598
+ # specified server in the same group.
599
+ #
600
+ # rackspace.share_ip_address(2296, 42, "174.143.56.6") #=> true
601
+ #
602
+ # rackspace.get_server(2290) #=>
603
+ # {"server"=>
604
+ # {"name"=>"my-awesome-server",
605
+ # "addresses"=>
606
+ # {"public"=>["174.143.56.6", "174.143.56.13"], "private"=>["10.176.1.235"]},
607
+ # "progress"=>100,
608
+ # "imageId"=>8,
609
+ # "metadata"=>{"KD1"=>"XXXX1", "KD2"=>"XXXX2"},
610
+ # "id"=>2290,
611
+ # "flavorId"=>3,
612
+ # "hostId"=>"1d5fa1271f57354d9e2861e848568eb3",
613
+ # "status"=>"SHARE_IP_NO_CONFIG"}}
614
+ #
615
+ def share_ip_address(server_id, shared_ip_group_id, address, configure_server=true, opts={})
616
+ body = {
617
+ 'shareIp' => {
618
+ 'sharedIpGroupId' => shared_ip_group_id,
619
+ 'configureServer' => configure_server
620
+ }
621
+ }
622
+ api(:put, "/servers/#{server_id}/ips/public/#{address}", opts.merge(:body => body.to_json))
623
+ end
624
+
625
+ # Remove a shared IP address from the specified server
626
+ #
627
+ # rackspace.unshare_ip_address(2296, "174.143.56.6") #=> true
628
+ #
629
+ def unshare_ip_address(server_id, address, opts={})
630
+ body = { 'unshareIp' => { 'addr' => address } }
631
+ api(:delete, "/servers/#{server_id}/ips/public/#{address}", opts.merge(:body => body.to_json))
632
+ end
633
+
634
+ # Delete a server.
635
+ # Returns +true+ on success.
636
+ #
637
+ # rackspace.delete_server(2284) #=> true
638
+ #
639
+ def delete_server(server_id, opts={})
640
+ api(:delete, "/servers/#{server_id}", opts)
641
+ end
642
+
643
+ #--------------------------------
644
+ # Backup Schedules
645
+ #--------------------------------
646
+
647
+ # Get the backup schedule for the specified server.
648
+ #
649
+ # rackspace.get_backup_schedule(62844) #=>
650
+ # {"backupSchedule"=>{"weekly"=>"DISABLED", "enabled"=>false, "daily"=>"DISABLED"}}
651
+ #
652
+ def get_backup_schedule(server_id, opts={})
653
+ api(:get, "/servers/#{server_id}/backup_schedule", opts)
654
+ end
655
+
656
+ # Create a new backup schedule or updates an existing backup schedule for the specified server.
657
+ # +Schedule_data+ is a hash: :enabled, :daily, :weekly
658
+ #
659
+ # # set just a daily backup
660
+ # rackspace.update_backup_schedule(62844, {:enabled => true, :daily => "H_0400_0600"}) #=> true
661
+ #
662
+ # # set both the weekly and the daily schedules
663
+ # h.update_backup_schedule(62844, {:enabled => true, :daily => "H_0400_0600", :weekly => 'MONDAY'}) #=> true
664
+ #
665
+ # # disable (delete) the schedule
666
+ # h.update_backup_schedule(62844, {:enabled => false}) #=> true
667
+ #
668
+ # P.S. the changes may appear in some seconds
669
+ #
670
+ def update_backup_schedule(server_id, schedule_data={}, opts={})
671
+ body = { 'backupSchedule' => { 'enabled' => schedule_data[:enabled] ? true : false } }
672
+ daily = schedule_data[:daily].blank? ? 'DISABLED' : schedule_data[:daily].to_s.upcase
673
+ weekly = schedule_data[:weekly].blank? ? 'DISABLED' : schedule_data[:weekly].to_s.upcase
674
+ body['backupSchedule']['daily'] = daily
675
+ body['backupSchedule']['weekly'] = weekly
676
+ api(:post, "/servers/#{server_id}/backup_schedule", opts.merge(:body => body.to_json))
677
+ end
678
+
679
+ # Deletes the backup schedule for the specified server.
680
+ #
681
+ # h.delete_backup_schedule(62844) #=> true
682
+ #
683
+ # P.S. the changes may appear in some seconds
684
+ #
685
+ def delete_backup_schedule(server_id, opts={})
686
+ api(:delete, "/servers/#{server_id}/backup_schedule", opts)
687
+ end
688
+
689
+ #--------------------------------
690
+ # Shared IP Groups
691
+ #--------------------------------
692
+
693
+ # List shared IP groups. Options: :detail => false|true.
694
+ #
695
+ # RightRackspace caching: yes, keys: '/shared_ip_groups', '/shared_ip_groups/detail'
696
+ #
697
+ def list_shared_ip_groups(opts={})
698
+ api_or_cache(:get, detailed_path("/shared_ip_groups", opts), opts.merge(:incrementally => PAGINATION_ENABLED))
699
+ end
700
+
701
+ # Incrementally list IP groups.
702
+ #
703
+ # # list groups by 5
704
+ # rackspace.incrementally_list_shared_ip_groups(0, 5) do |x|
705
+ # puts x.inspect
706
+ # true
707
+ # end
708
+ #
709
+ def incrementally_list_shared_ip_groups(offset=nil, limit=nil, opts={}, &block)
710
+ incrementally_list_resources(:get, detailed_path("/shared_ip_groups", opts), offset, limit, opts, &block)
711
+ end
712
+
713
+ # Create a new shared IP group.
714
+ #
715
+ # rackspace.create_shared_ip_group('my_awesome_group', 2290) #=>
716
+ # {"sharedIpGroup"=>{"name"=>"my_awesome_group", "id"=>42}}
717
+ #
718
+ def create_shared_ip_group(name, server_id=nil, opts={})
719
+ body = { 'sharedIpGroup' => { 'name' => name } }
720
+ body['sharedIpGroup']['server'] = server_id unless server_id.blank?
721
+ api(:post, "/shared_ip_groups", opts.merge(:body => body.to_json))
722
+ end
723
+
724
+ # Get shared IP group data.
725
+ #
726
+ # rackspace.list_shared_ip_groups #=>
727
+ # {"sharedIpGroups"=>[{"name"=>"my_awesome_group", "id"=>42, "servers"=>[2290]}]}
728
+ #
729
+ def get_shared_ip_group(shared_ip_group_id, opts={})
730
+ api(:get, "/shared_ip_groups/#{shared_ip_group_id}", opts)
731
+ end
732
+
733
+ # Delete an IP group.
734
+ #
735
+ # rackspace.delete_shared_ip_group(42) #=> true
736
+ #
737
+ def delete_shared_ip_group(shared_ip_group_id, opts={})
738
+ api(:delete, "/shared_ip_groups/#{shared_ip_group_id}", opts)
739
+ end
740
+
741
+ end
742
+ end
743
+ end