right_slicehost 0.1.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.
@@ -0,0 +1,4 @@
1
+ === 0.1.0 / 2009-20-02
2
+
3
+ * Initial release
4
+
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/right_slicehost.rb
6
+ lib/slicehost_base.rb
7
+ lib/benchmark_fix.rb
8
+ lib/support.rb
9
+ test/test_right_slicehost.rb
10
+ test/_test_helper.rb
11
+ test/_test_credentials.rb
@@ -0,0 +1,47 @@
1
+ = RightScale Slicehost API Ruby Gem
2
+
3
+ Published by RightScale, Inc. under the MIT License.
4
+ For information about RightScale, see http://www.rightscale.com
5
+
6
+ == DESCRIPTION:
7
+
8
+ The RightScale Slicehost gem has been designed to provide a robust interface to Slicehost's existing API.
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ - Full programmatic access to the Slicehost API.
13
+ - Complete error handling: all operations check for errors and report complete
14
+ error information by raising a SlicehostError.
15
+ - Persistent HTTP connections with robust network-level retry layer using
16
+ Rightscale::HttpConnection. This includes socket timeouts and retries.
17
+ - Robust HTTP-level retry layer. Certain (user-adjustable) HTTP errors
18
+ returned by Slicehost are classified as temporary errors.
19
+ These errors are automaticallly retried using exponentially increasing intervals.
20
+ The number of retries is user-configurable.
21
+
22
+ == INSTALL:
23
+
24
+ sudo gem install right_slicehost
25
+
26
+ == LICENSE:
27
+
28
+ Copyright (c) 2007-2009 RightScale, Inc.
29
+
30
+ Permission is hereby granted, free of charge, to any person obtaining
31
+ a copy of this software and associated documentation files (the
32
+ 'Software'), to deal in the Software without restriction, including
33
+ without limitation the rights to use, copy, modify, merge, publish,
34
+ distribute, sublicense, and/or sell copies of the Software, and to
35
+ permit persons to whom the Software is furnished to do so, subject to
36
+ the following conditions:
37
+
38
+ The above copyright notice and this permission notice shall be
39
+ included in all copies or substantial portions of the Software.
40
+
41
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
42
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
43
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
44
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
45
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
46
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
47
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ require "rake/testtask"
4
+ require 'rcov/rcovtask'
5
+ $: << File.dirname(__FILE__)
6
+ require './lib/right_slicehost.rb'
7
+
8
+ # Suppress Hoe's self-inclusion as a dependency for our Gem. This also keeps
9
+ # Rake & rubyforge out of the dependency list. Users must manually install
10
+ # these gems to run tests, etc.
11
+ # TRB 2/20/09: also do this for the extra_dev_deps array present in newer hoes.
12
+ # Older versions of RubyGems will try to install developer-dependencies as
13
+ # required runtime dependencies....
14
+ class Hoe
15
+ def extra_deps
16
+ @extra_deps.reject do |x|
17
+ Array(x).first == 'hoe'
18
+ end
19
+ end
20
+ def extra_dev_deps
21
+ @extra_dev_deps.reject do |x|
22
+ Array(x).first == 'hoe'
23
+ end
24
+ end
25
+ end
26
+
27
+ Hoe.new('right_slicehost', RightSlicehost::VERSION) do |p|
28
+ p.rubyforge_name = 'rightscale'
29
+ p.author = 'RightScale, Inc.'
30
+ p.email = 'rubygems@rightscale.com'
31
+ p.summary = 'Interface classes for the Slicehost API'
32
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
33
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
34
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
35
+ p.remote_rdoc_dir = "/right_slicehost_gem_doc"
36
+ p.extra_deps = [['right_http_connection','>= 1.2.4']]
37
+ end
@@ -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
@@ -0,0 +1,601 @@
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
+ require 'cgi'
24
+ require 'benchmark'
25
+ require 'md5'
26
+ require 'rubygems'
27
+ require "rexml/document"
28
+ require 'right_http_connection'
29
+
30
+ $:.unshift(File.dirname(__FILE__))
31
+ require 'benchmark_fix'
32
+ require 'support'
33
+ require 'slicehost_base'
34
+
35
+
36
+ module RightSlicehost
37
+ MAJOR = 0
38
+ MINOR = 1
39
+ TINY = 0
40
+ VERSION = [MAJOR, MINOR, TINY].join('.')
41
+ end
42
+
43
+ module Rightscale
44
+
45
+ # # Slices:
46
+ #
47
+ # sl = Rightscale::Slicehost.new('12345...uvwxyz')
48
+ #
49
+ # sl.list_images #=>
50
+ # [{:sls_id=>2, :name=>"CentOS 5.2"},
51
+ # {:sls_id=>3, :name=>"Gentoo 2008.0"},
52
+ # {:sls_id=>4, :name=>"Debian 4.0 (etch)"},
53
+ # {:sls_id=>5, :name=>"Fedora 9"},
54
+ # {:sls_id=>9, :name=>"Arch 2007.08"},
55
+ # {:sls_id=>10, :name=>"Ubuntu 8.04.1 LTS (hardy)"},
56
+ # {:sls_id=>11, :name=>"Ubuntu 8.10 (intrepid)"}]
57
+ #
58
+ # sl.list_flavors #=>
59
+ # [{:sls_id=>1, :name=>"256 slice", :price=>2000, :ram=>256},
60
+ # {:sls_id=>2, :name=>"512 slice", :price=>3800, :ram=>512},
61
+ # {:sls_id=>3, :name=>"1GB slice", :price=>7000, :ram=>1024},
62
+ # {:sls_id=>4, :name=>"2GB slice", :price=>13000, :ram=>2048},
63
+ # {:sls_id=>5, :name=>"4GB slice", :price=>25000, :ram=>4096},
64
+ # {:sls_id=>6, :name=>"8GB slice", :price=>45000, :ram=>8192},
65
+ # {:sls_id=>7, :name=>"15.5GB slice", :price=>80000, :ram=>15872}]
66
+ #
67
+ # sl.create_slice(:flavor_id=>1 , :image_id=>2, :name=>'my-slice' ) #=>
68
+ # {:flavor_sls_id=>1,
69
+ # :addresses=>["173.45.224.125"],
70
+ # :bw_in=>0.0,
71
+ # :sls_id=>26831,
72
+ # :name=>"my-slice",
73
+ # :status=>"build",
74
+ # :bw_out=>0.0,
75
+ # :ip_address=>"173.45.224.125",
76
+ # :progress=>0,
77
+ # :image_sls_id=>2,
78
+ # :root_password=>"my-slicen57"}
79
+ #
80
+ # sl.rebuild_slice(26831, :image_id => 3) #=> true
81
+ #
82
+ # sl.reboot_slice(26832, :hard) #=> true
83
+ #
84
+ # sl.delete_slice(26832) #=> true
85
+ #
86
+ # # DNS:
87
+ #
88
+ # sl.list_zones #=>
89
+ # [ {:origin=>"a1.my-domain.com.", :ttl=>300, :sls_id=>45486, :active=>true},
90
+ # {:origin=>"a2.my-domain.com.", :ttl=>300, :sls_id=>45485, :active=>true},
91
+ # {:origin=>"a3.my-domain.com.", :ttl=>300, :sls_id=>45487, :active=>false}, ... ]
92
+ #
93
+ # sl.list_records #=>
94
+ # [ { :sls_id=>"348257",
95
+ # :zone_id=>45687,
96
+ # :data=>"woo-hoo.my-domain.com",
97
+ # :aux=>"0",
98
+ # :name=>"wooooohooooo",
99
+ # :ttl=>86400,
100
+ # :active=>true,
101
+ # :record_type=>"CNAME"}, ... ]
102
+ #
103
+ class Slicehost
104
+ include RightSlicehostInterface
105
+
106
+ def initialize(slicehost_password=nil, params={})
107
+ slicehost_password ||= ENV['SLICEHOST_PASSWORD']
108
+ init slicehost_password, params
109
+ end
110
+
111
+ def build_path(path, sls_id=nil, action=nil) # :nodoc:
112
+ path = path.to_s
113
+ unless (sls_id || action)
114
+ path += '.xml'
115
+ else
116
+ path += "/#{sls_id}#{action ? '' : '.xml'}" if sls_id
117
+ path += "/#{action}.xml" if action
118
+ end
119
+ path
120
+ end
121
+
122
+ def build_xml(name, params) # :nodoc:
123
+ xml_params = params.to_a.map do |key,value|
124
+ key = key.to_s.gsub('_','-')
125
+ "<#{key}>#{CGI.escape(value.to_s)}</#{key}>"
126
+ end.join("\n")
127
+
128
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
129
+ "<#{name.to_s}>\n" +
130
+ "#{xml_params}\n" +
131
+ "</#{name.to_s}>\n"
132
+ end
133
+
134
+ #------------------------------------------------------------
135
+ # Images
136
+ #------------------------------------------------------------
137
+
138
+ # List images.
139
+ #
140
+ # sl.list_images #=>
141
+ # [{:sls_id=>2, :name=>"CentOS 5.2"},
142
+ # {:sls_id=>3, :name=>"Gentoo 2008.0"},
143
+ # {:sls_id=>4, :name=>"Debian 4.0 (etch)"},
144
+ # {:sls_id=>5, :name=>"Fedora 9"},
145
+ # {:sls_id=>9, :name=>"Arch 2007.08"},
146
+ # {:sls_id=>10, :name=>"Ubuntu 8.04.1 LTS (hardy)"},
147
+ # {:sls_id=>11, :name=>"Ubuntu 8.10 (intrepid)"}]
148
+ #
149
+ # sl.list_images(9) #=> {:sls_id=>9, :name=>"Arch 2007.08"}
150
+ #
151
+ def list_images(sls_id=nil)
152
+ req = generate_request(Net::HTTP::Get, build_path(:images, sls_id))
153
+ result = request_cache_or_info(:list_images, req, ImagesParser, sls_id.nil?)
154
+ sls_id ? result.first : result
155
+ rescue
156
+ on_exception
157
+ end
158
+
159
+ #------------------------------------------------------------
160
+ # Flavors
161
+ #------------------------------------------------------------
162
+
163
+ # List flavors.
164
+ #
165
+ # sl.list_flavors #=>
166
+ # [{:sls_id=>1, :name=>"256 slice", :price=>2000, :ram=>256},
167
+ # {:sls_id=>2, :name=>"512 slice", :price=>3800, :ram=>512},
168
+ # {:sls_id=>3, :name=>"1GB slice", :price=>7000, :ram=>1024},
169
+ # {:sls_id=>4, :name=>"2GB slice", :price=>13000, :ram=>2048},
170
+ # {:sls_id=>5, :name=>"4GB slice", :price=>25000, :ram=>4096},
171
+ # {:sls_id=>6, :name=>"8GB slice", :price=>45000, :ram=>8192},
172
+ # {:sls_id=>7, :name=>"15.5GB slice", :price=>80000, :ram=>15872}]
173
+ #
174
+ # sl.list_flavors(6) #=> {:sls_id=>6, :name=>"8GB slice", :price=>45000, :ram=>8192}
175
+ #
176
+ def list_flavors(sls_id=nil)
177
+ req = generate_request(Net::HTTP::Get, build_path(:flavors, sls_id))
178
+ result = request_cache_or_info(:list_flavors, req, FlavorsParser, sls_id.nil?)
179
+ sls_id ? result.first : result
180
+ rescue
181
+ on_exception
182
+ end
183
+
184
+ #------------------------------------------------------------
185
+ # Backups
186
+ #------------------------------------------------------------
187
+
188
+ # List backups.
189
+ #
190
+ # sl.list_backups #=>
191
+ # [{:sls_id=>"5-6507",
192
+ # :slice_sls_id=>26831,
193
+ # :name=>"backup 1",
194
+ # :date=>Wed Dec 10 00:43:20 UTC 2008}, ...]
195
+ #
196
+ # sl.list_backups("5-6507") #=>
197
+ # {:sls_id=>"5-6507",
198
+ # :slice_sls_id=>26831,
199
+ # :name=>"backup 1",
200
+ # :date=>Wed Dec 10 00:43:20 UTC 2008}
201
+ #
202
+ #
203
+ #
204
+ def list_backups(sls_id=nil)
205
+ req = generate_request(Net::HTTP::Get, build_path(:backups, sls_id))
206
+ result = request_cache_or_info(:list_backups, req, BackupsParser, sls_id.nil?)
207
+ sls_id ? result.first : result
208
+ rescue
209
+ on_exception
210
+ end
211
+
212
+ #------------------------------------------------------------
213
+ # Slices
214
+ #------------------------------------------------------------
215
+
216
+ # List slices.
217
+ #
218
+ # sl.list_slices #=>
219
+ # [{:bw_in=>0.05,
220
+ # :sls_id=>26706,
221
+ # :bw_out=>0.0,
222
+ # :ip_address=>"173.45.233.125",
223
+ # :progress=>100,
224
+ # :status=>"active",
225
+ # :name=>"slice26706",
226
+ # :image_sls_id=>11,
227
+ # :flavor_sls_id=>1,
228
+ # :addresses=>["173.45.233.125"]}, ...]
229
+ #
230
+ def list_slices(sls_id=nil)
231
+ # GET /slices.xml
232
+ req = generate_request(Net::HTTP::Get, build_path(:slices, sls_id))
233
+ result = request_cache_or_info(:list_slices, req, SlicesParser, sls_id.nil?)
234
+ sls_id ? result.first : result
235
+ rescue
236
+ on_exception
237
+ end
238
+
239
+ # Create a new slice.
240
+ #
241
+ # sl.create_slice(:flavor_id=>1 , :image_id=>2, :name=>'my-slice' ) #=>
242
+ # {:flavor_sls_id=>1,
243
+ # :addresses=>["173.45.224.125"],
244
+ # :bw_in=>0.0,
245
+ # :sls_id=>26831,
246
+ # :name=>"my-slice",
247
+ # :status=>"build",
248
+ # :bw_out=>0.0,
249
+ # :ip_address=>"173.45.224.125",
250
+ # :progress=>0,
251
+ # :image_sls_id=>2,
252
+ # :root_password=>"my-slicen57"}
253
+ #
254
+ def create_slice(params={})
255
+ # POST /slices.xml
256
+ req = generate_request(Net::HTTP::Post, build_path(:slices))
257
+ req[:request].body = build_xml(:slice, params)
258
+ request_info(req, SlicesParser.new(:logger => @logger)).first
259
+ end
260
+
261
+ # Update a slice.
262
+ #
263
+ # sl.update_slice(26831, :name => 'my-awesome-slice') #=> true
264
+ #
265
+ def update_slice(sls_id, params={})
266
+ # PUT /slices/id.xml
267
+ req = generate_request(Net::HTTP::Put, build_path(:slices, sls_id))
268
+ req[:request].body = build_xml(:slice, params)
269
+ request_info(req, RightHttp2xxParser.new(:logger => @logger))
270
+ end
271
+
272
+ # Rebuild a slice.
273
+ #
274
+ # sl.rebuild_slice(26831, :image_id => 3) #=> true
275
+ #
276
+ def rebuild_slice(sls_id, params)
277
+ # PUT /slices/id/rebuild.xml?params
278
+ req = generate_request(Net::HTTP::Put, build_path(:slices, sls_id, :rebuild), params)
279
+ request_info(req, RightHttp2xxParser.new)
280
+ rescue
281
+ on_exception
282
+ end
283
+
284
+ # Reboot a slice (soft reboot is by default).
285
+ #
286
+ # sl.reboot_slice(26831) #=> true
287
+ # sl.reboot_slice(26832, :hard) #=> true
288
+ #
289
+ def reboot_slice(sls_id, hard_reboot = false)
290
+ # PUT /slices/id/reboot.xml
291
+ action = hard_reboot ? :hard_reboot : :reboot
292
+ req = generate_request(Net::HTTP::Put, build_path(:slices, sls_id, action))
293
+ request_info(req, RightHttp2xxParser.new)
294
+ rescue
295
+ on_exception
296
+ end
297
+
298
+ # Delete a slice.
299
+ #
300
+ # sl.delete_slice(26831) #=> true
301
+ #
302
+ def delete_slice(sls_id)
303
+ # DELETE /slices/id.xml
304
+ req = generate_request(Net::HTTP::Delete, build_path(:slices, sls_id))
305
+ request_info(req, RightHttp2xxParser.new(:logger => @logger))
306
+ end
307
+
308
+
309
+ #------------------------------------------------------------
310
+ # Zones
311
+ #------------------------------------------------------------
312
+
313
+ # List DNS Zones.
314
+ #
315
+ # # bunch of zones
316
+ # sl.list_zones #=>
317
+ # [ {:origin=>"a1.my-domain.com.", :ttl=>300, :sls_id=>45486, :active=>true},
318
+ # {:origin=>"a2.my-domain.com.", :ttl=>300, :sls_id=>45485, :active=>true},
319
+ # {:origin=>"a3.my-domain.com.", :ttl=>300, :sls_id=>45487, :active=>false} ]
320
+ #
321
+ # # single zone
322
+ # sl.list_zones(45486) #=>
323
+ # {:origin=>"a1.my-domain.com.", :ttl=>300, :sls_id=>45486, :active=>true}
324
+ #
325
+ def list_zones(sls_id=nil)
326
+ # GET /zones.xml
327
+ # GET /zones/id.xml
328
+ req = generate_request(Net::HTTP::Get, build_path(:zones, sls_id))
329
+ result = request_cache_or_info(:list_zones, req, ZonesParser, sls_id.nil?)
330
+ sls_id ? result.first : result
331
+ rescue
332
+ on_exception
333
+ end
334
+
335
+ # Create a new zone.
336
+ #
337
+ # sl.create_zone(:origin => 'a4.my_domain.ru', :ttl => 111, :active => false) #=>
338
+ # { :origin=>"a4.my-domain.com.",
339
+ # :ttl=>111,
340
+ # :sls_id=>45689,
341
+ # :active=>false}
342
+ #
343
+ def create_zone(params={})
344
+ params[:active] = (params[:active] ? 'Y' : 'N') unless params[:active].nil?
345
+ # POST /zones.xml
346
+ req = generate_request(Net::HTTP::Post, build_path(:zones))
347
+ req[:request].body = build_xml(:zone, params)
348
+ request_info(req, ZonesParser.new(:logger => @logger)).first
349
+ end
350
+
351
+ # Update a zone.
352
+ #
353
+ # sl.update_zone(45486, :acive => false, :ttl => 333) #=> true
354
+ #
355
+ def update_zone(sls_id, params={})
356
+ params[:active] = (params[:active] ? 'Y' : 'N') unless params[:active].nil?
357
+ # PUT /zones/id.xml
358
+ req = generate_request(Net::HTTP::Put, build_path(:zones, sls_id))
359
+ req[:request].body = build_xml(:zone, params)
360
+ request_info(req, RightHttp2xxParser.new(:logger => @logger))
361
+ end
362
+
363
+ # Delete a zone.
364
+ #
365
+ # sl.delete_zone(45486) #=> true
366
+ #
367
+ def delete_zone(sls_id)
368
+ # DELETE /zones/id.xml
369
+ req = generate_request(Net::HTTP::Delete, build_path(:zones, sls_id))
370
+ request_info(req, RightHttp2xxParser.new(:logger => @logger))
371
+ end
372
+
373
+ #------------------------------------------------------------
374
+ # Records
375
+ #------------------------------------------------------------
376
+
377
+ # List DNS Records
378
+ #
379
+ # sl.list_records #=>
380
+ # [ { :sls_id=>"348257",
381
+ # :zone_id=>45687,
382
+ # :data=>"woo-hoo.my-domain.com",
383
+ # :aux=>"0",
384
+ # :name=>"wooooohooooo",
385
+ # :ttl=>86400,
386
+ # :active=>true,
387
+ # :record_type=>"CNAME"}, ... ]
388
+ #
389
+ def list_records(sls_id=nil)
390
+ # GET /records.xml
391
+ # GET /records/id.xml
392
+ req = generate_request(Net::HTTP::Get, build_path(:records, sls_id))
393
+ result = request_cache_or_info(:list_records, req, RecordsParser, sls_id.nil?)
394
+ sls_id ? result.first : result
395
+ rescue
396
+ on_exception
397
+ end
398
+
399
+ # Create a new record.
400
+ #
401
+ # sl.create_record(:zone_id => 45687, :data=>"woo-hoo.my-domain.com", :name=>"wooooohooooo", :data=>"woo-hoo.my-domain.com") #=>
402
+ # [ { :sls_id=>348257,
403
+ # :zone_id=>45687,
404
+ # :data=>"woo-hoo.my-domain.com",
405
+ # :aux=>"0",
406
+ # :name=>"wooooohooooo",
407
+ # :ttl=>86400,
408
+ # :active=>true,
409
+ # :record_type=>"CNAME"}, ... ]
410
+ #
411
+ def create_record(params={})
412
+ params[:active] = (params[:active] ? 'Y' : 'N') unless params[:active].nil?
413
+ # POST /records.xml
414
+ req = generate_request(Net::HTTP::Post, build_path(:records))
415
+ req[:request].body = build_xml(:record, params)
416
+ request_info(req, RecordsParser.new(:logger => @logger)).first
417
+ end
418
+
419
+ # Update a record.
420
+ #
421
+ # sl.update_record(348257, :ttl => 777, :data=>"oops.my-domain.com") #=> true
422
+ #
423
+ def update_record(sls_id, params={})
424
+ params[:active] = (params[:active] ? 'Y' : 'N') unless params[:active].nil?
425
+ # PUT /zones/id.xml
426
+ req = generate_request(Net::HTTP::Put, build_path(:records, sls_id))
427
+ req[:request].body = build_xml(:record, params)
428
+ request_info(req, RightHttp2xxParser.new(:logger => @logger))
429
+ end
430
+
431
+ # Delete a record.
432
+ #
433
+ # sl.delete_record(348257) #=> true
434
+ #
435
+ def delete_record(sls_id)
436
+ # DELETE /records/id.xml
437
+ req = generate_request(Net::HTTP::Delete, build_path(:records, sls_id))
438
+ request_info(req, RightHttp2xxParser.new(:logger => @logger))
439
+ end
440
+
441
+ #------------------------------------------------------------
442
+ # Parsers
443
+ #------------------------------------------------------------
444
+
445
+ #------------------------------------------------------------
446
+ # Images
447
+ #------------------------------------------------------------
448
+
449
+ class ImagesParser < RightSlicehostParser #:nodoc:
450
+ def tagstart(name, attributes)
451
+ @item = {} if name == 'image'
452
+ end
453
+ def tagend(name)
454
+ case name
455
+ when 'id' then @item[:sls_id] = @text.to_i
456
+ when 'name' then @item[:name] = @text
457
+ when 'image' then @result << @item
458
+ end
459
+ end
460
+ def reset
461
+ @result = []
462
+ end
463
+ end
464
+
465
+ #------------------------------------------------------------
466
+ # Flavors
467
+ #------------------------------------------------------------
468
+
469
+ class FlavorsParser < RightSlicehostParser #:nodoc:
470
+ def tagstart(name, attributes)
471
+ @item = {} if name == 'flavor'
472
+ end
473
+ def tagend(name)
474
+ case name
475
+ when 'id' then @item[:sls_id] = @text.to_i
476
+ when 'name' then @item[:name] = @text
477
+ when 'price' then @item[:price] = @text.to_i
478
+ when 'ram' then @item[:ram] = @text.to_i
479
+ when 'flavor' then @result << @item
480
+ end
481
+ end
482
+ def reset
483
+ @result = []
484
+ end
485
+ end
486
+
487
+ #------------------------------------------------------------
488
+ # Backups
489
+ #------------------------------------------------------------
490
+
491
+ class BackupsParser < RightSlicehostParser #:nodoc:
492
+ def tagstart(name, attributes)
493
+ @item = {} if name == 'backup'
494
+ end
495
+ def tagend(name)
496
+ case name
497
+ when 'id' then @item[:sls_id] = @text
498
+ when 'name' then @item[:name] = @text
499
+ when 'slice_id' then @item[:slice_sls_id] = @text.to_i
500
+ when 'date' then @item[:date] = Time.parse(@text)
501
+ when 'backup' then @result << @item
502
+ end
503
+ end
504
+ def reset
505
+ @result = []
506
+ end
507
+ end
508
+
509
+ #------------------------------------------------------------
510
+ # Slices
511
+ #------------------------------------------------------------
512
+
513
+ class SlicesParser < RightSlicehostParser #:nodoc:
514
+ def tagstart(name, attributes)
515
+ @item = {} if name == 'slice'
516
+ @addresses = [] if name == 'addresses'
517
+ end
518
+ def tagend(name)
519
+ case name
520
+ when 'id' then @item[:sls_id] = @text.to_i
521
+ when 'name' then @item[:name] = @text
522
+ when 'image-id' then @item[:image_sls_id] = @text.to_i
523
+ when 'flavor-id' then @item[:flavor_sls_id] = @text.to_i
524
+ when 'slice-id' then @item[:slice_sls_id] = @text.to_i
525
+ when 'backup-id' then @item[:backup_sls_id] = @text.to_i
526
+ when 'status' then @item[:status] = @text
527
+ when 'progress' then @item[:progress] = @text.to_i
528
+ when 'bw-in' then @item[:bw_in] = @text.to_f
529
+ when 'bw-out' then @item[:bw_out] = @text.to_f
530
+ when 'ip-address' then @item[:ip_address] = @text
531
+ when 'root-password' then @item[:root_password] = @text
532
+ when 'address' then @addresses << @text
533
+ when 'addresses' then @item[:addresses] = @addresses
534
+ when 'slice' then @result << @item
535
+ end
536
+ end
537
+ def reset
538
+ @result = []
539
+ end
540
+ end
541
+
542
+ #------------------------------------------------------------
543
+ # Zones
544
+ #------------------------------------------------------------
545
+
546
+ class ZonesParser < RightSlicehostParser #:nodoc:
547
+ def tagstart(name, attributes)
548
+ @item = {} if name == 'zone'
549
+ end
550
+ def tagend(name)
551
+ case name
552
+ when 'id' then @item[:sls_id] = @text.to_i
553
+ when 'origin' then @item[:origin] = @text
554
+ when 'ttl' then @item[:ttl] = @text.to_i
555
+ when 'active' then @item[:active] = @text == 'Y'
556
+ when 'zone' then @result << @item
557
+ end
558
+ end
559
+ def reset
560
+ @result = []
561
+ end
562
+ end
563
+
564
+ #------------------------------------------------------------
565
+ # Records
566
+ #------------------------------------------------------------
567
+
568
+ class RecordsParser < RightSlicehostParser #:nodoc:
569
+ def tagstart(name, attributes)
570
+ @item = {} if name == 'record'
571
+ end
572
+ def tagend(name)
573
+ case name
574
+ when 'id' then @item[:sls_id] = @text.to_i
575
+ when 'record-type' then @item[:record_type] = @text
576
+ when 'zone-id' then @item[:zone_id] = @text.to_i
577
+ when 'name' then @item[:name] = @text
578
+ when 'data' then @item[:data] = @text
579
+ when 'ttl' then @item[:ttl] = @text.to_i
580
+ when 'active' then @item[:active] = @text == 'Y'
581
+ when 'aux' then @item[:aux] = @text
582
+ when 'record' then @result << @item
583
+ end
584
+ end
585
+ def reset
586
+ @result = []
587
+ end
588
+ end
589
+
590
+ #------------------------------------------------------------
591
+ # HTTP 2xx
592
+ #------------------------------------------------------------
593
+
594
+ class RightHttp2xxParser < RightSlicehostParser # :nodoc:
595
+ def parse(response)
596
+ @result = response.is_a?(Net::HTTPSuccess)
597
+ end
598
+ end
599
+
600
+ end
601
+ end