right_rackspace 0.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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