right_rackspace 0.0.0.1

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