deltacloud-client 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.
Files changed (47) hide show
  1. data/COPYING +502 -0
  2. data/Rakefile +39 -0
  3. data/credentials.yml +2 -0
  4. data/init.rb +19 -0
  5. data/lib/dcloud/base_model.rb +80 -0
  6. data/lib/dcloud/flavor.rb +43 -0
  7. data/lib/dcloud/image.rb +46 -0
  8. data/lib/dcloud/instance.rb +97 -0
  9. data/lib/dcloud/realm.rb +47 -0
  10. data/lib/dcloud/state.rb +30 -0
  11. data/lib/dcloud/storage_snapshot.rb +50 -0
  12. data/lib/dcloud/storage_volume.rb +53 -0
  13. data/lib/dcloud/transition.rb +35 -0
  14. data/lib/deltacloud.rb +356 -0
  15. data/specs/data/images/img1.yml +3 -0
  16. data/specs/data/images/img2.yml +3 -0
  17. data/specs/data/images/img3.yml +3 -0
  18. data/specs/data/instances/inst1.yml +8 -0
  19. data/specs/data/instances/inst2.yml +8 -0
  20. data/specs/data/storage_snapshots/snap1.yml +4 -0
  21. data/specs/data/storage_snapshots/snap2.yml +4 -0
  22. data/specs/data/storage_snapshots/snap3.yml +4 -0
  23. data/specs/data/storage_volumes/vol1.yml +6 -0
  24. data/specs/data/storage_volumes/vol2.yml +6 -0
  25. data/specs/data/storage_volumes/vol3.yml +6 -0
  26. data/specs/fixtures/images/img1.yml +3 -0
  27. data/specs/fixtures/images/img2.yml +3 -0
  28. data/specs/fixtures/images/img3.yml +3 -0
  29. data/specs/fixtures/instances/inst1.yml +8 -0
  30. data/specs/fixtures/instances/inst2.yml +8 -0
  31. data/specs/fixtures/storage_snapshots/snap1.yml +4 -0
  32. data/specs/fixtures/storage_snapshots/snap2.yml +4 -0
  33. data/specs/fixtures/storage_snapshots/snap3.yml +4 -0
  34. data/specs/fixtures/storage_volumes/vol1.yml +6 -0
  35. data/specs/fixtures/storage_volumes/vol2.yml +6 -0
  36. data/specs/fixtures/storage_volumes/vol3.yml +6 -0
  37. data/specs/flavors_spec.rb +67 -0
  38. data/specs/images_spec.rb +104 -0
  39. data/specs/initialization_spec.rb +59 -0
  40. data/specs/instance_states_spec.rb +77 -0
  41. data/specs/instances_spec.rb +171 -0
  42. data/specs/realms_spec.rb +65 -0
  43. data/specs/shared/resources.rb +29 -0
  44. data/specs/spec_helper.rb +54 -0
  45. data/specs/storage_snapshot_spec.rb +76 -0
  46. data/specs/storage_volume_spec.rb +86 -0
  47. metadata +108 -0
@@ -0,0 +1,53 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ require 'dcloud/base_model'
19
+
20
+ module DCloud
21
+ class StorageVolume < BaseModel
22
+
23
+ xml_tag_name :storage_volume
24
+
25
+ attribute :created
26
+ attribute :state
27
+ attribute :capacity
28
+ attribute :device
29
+ attribute :instance
30
+
31
+ def initialize(client, uri, xml=nil)
32
+ super( client, uri, xml )
33
+ end
34
+
35
+ def load_payload(xml=nil)
36
+ super(xml)
37
+ unless xml.nil?
38
+ @created = xml.text( 'created' )
39
+ @state = xml.text( 'state' )
40
+ @capacity = xml.text( 'capacity' ).to_f
41
+ @device = xml.text( 'device' )
42
+ instances = xml.get_elements( 'instance' )
43
+ if ( ! instances.empty? )
44
+ instance = instances.first
45
+ instance_href = instance.attributes['href']
46
+ if ( instance_href )
47
+ @instance = Instance.new( @client, instance_href )
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,35 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+
19
+ module DCloud
20
+ class Transition
21
+
22
+ attr_accessor :to
23
+ attr_accessor :action
24
+
25
+ def initialize(to, action)
26
+ @to = to
27
+ @action = action
28
+ end
29
+
30
+ def auto?()
31
+ @action.nil?
32
+ end
33
+
34
+ end
35
+ end
data/lib/deltacloud.rb ADDED
@@ -0,0 +1,356 @@
1
+ #
2
+ # Copyright (C) 2009 Red Hat, Inc.
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ require 'rest_client'
19
+ require 'rexml/document'
20
+ require 'logger'
21
+ require 'dcloud/flavor'
22
+ require 'dcloud/realm'
23
+ require 'dcloud/image'
24
+ require 'dcloud/instance'
25
+ require 'dcloud/storage_volume'
26
+ require 'dcloud/storage_snapshot'
27
+ require 'dcloud/state'
28
+ require 'dcloud/transition'
29
+ require 'base64'
30
+
31
+ module RestClient
32
+ class Response
33
+ def body
34
+ self.to_s
35
+ end
36
+ end
37
+ end
38
+
39
+ class DeltaCloud
40
+
41
+ attr_accessor :logger
42
+ attr_reader :api_uri
43
+ attr_reader :entry_points
44
+ attr_reader :driver_name
45
+
46
+ def self.driver_name(url)
47
+ DeltaCloud.new( nil, nil, url) do |client|
48
+ return client.driver_name
49
+ end
50
+ end
51
+
52
+ def initialize(name, password, api_uri, &block)
53
+ @logger = Logger.new( STDERR )
54
+ @name = name
55
+ @password = password
56
+ @api_uri = URI.parse( api_uri )
57
+ @entry_points = {}
58
+ discover_entry_points
59
+ connect( &block )
60
+ self
61
+ end
62
+
63
+
64
+ def connect(&block)
65
+ @http = RestClient::Resource.new( api_uri.to_s , :accept => 'application/xml' )
66
+ discover_entry_points
67
+ block.call( self ) if block
68
+ self
69
+ end
70
+
71
+ def api_host
72
+ @api_uri.host
73
+ end
74
+
75
+ def api_port
76
+ @api_uri.port
77
+ end
78
+
79
+ def api_path
80
+ @api_uri.path
81
+ end
82
+
83
+ def flavors(opts={})
84
+ flavors = []
85
+ request(entry_points[:flavors], :get, opts) do |response|
86
+ doc = REXML::Document.new( response )
87
+ doc.get_elements( 'flavors/flavor' ).each do |flavor|
88
+ uri = flavor.attributes['href']
89
+ flavors << DCloud::Flavor.new( self, uri, flavor )
90
+ end
91
+ end
92
+ flavors
93
+ end
94
+
95
+ def flavor(id)
96
+ request( entry_points[:flavors], :get, {:id=>id } ) do |response|
97
+ doc = REXML::Document.new( response.body )
98
+ doc.get_elements( '/flavor' ).each do |flavor|
99
+ uri = flavor.attributes['href']
100
+ return DCloud::Flavor.new( self, uri, flavor )
101
+ end
102
+ end
103
+ end
104
+
105
+ def fetch_flavor(uri)
106
+ xml = fetch_resource( :flavor, uri )
107
+ return DCloud::Flavor.new( self, uri, xml ) if xml
108
+ nil
109
+ end
110
+
111
+ def fetch_resource(type, uri)
112
+ request( uri ) do |response|
113
+ doc = REXML::Document.new( response.body )
114
+ if ( doc.root && ( doc.root.name == type.to_s.gsub( /_/, '-' ) ) )
115
+ return doc.root
116
+ end
117
+ end
118
+ nil
119
+ end
120
+
121
+ def instance_states
122
+ states = []
123
+ request( entry_points[:instance_states] ) do |response|
124
+ doc = REXML::Document.new( response.body )
125
+ doc.get_elements( 'states/state' ).each do |state_elem|
126
+ state = DCloud::State.new( state_elem.attributes['name'] )
127
+ state_elem.get_elements( 'transition' ).each do |transition_elem|
128
+ state.transitions << DCloud::Transition.new(
129
+ transition_elem.attributes['to'],
130
+ transition_elem.attributes['action']
131
+ )
132
+ end
133
+ states << state
134
+ end
135
+ end
136
+ states
137
+ end
138
+
139
+ def instance_state(name)
140
+ found = instance_states.find{|e| e.name.to_s == name.to_s}
141
+ found
142
+ end
143
+
144
+ def realms(opts={})
145
+ realms = []
146
+ request( entry_points[:realms], :get, opts ) do |response|
147
+ doc = REXML::Document.new( response.body )
148
+ doc.get_elements( 'realms/realm' ).each do |realm|
149
+ uri = realm.attributes['href']
150
+ realms << DCloud::Realm.new( self, uri, realm )
151
+ end
152
+ end
153
+ realms
154
+ end
155
+
156
+ def realm(id)
157
+ request( entry_points[:realms], :get, {:id=>id } ) do |response|
158
+ doc = REXML::Document.new( response.body )
159
+ doc.get_elements( 'realm' ).each do |realm|
160
+ uri = realm.attributes['href']
161
+ return DCloud::Realm.new( self, uri, realm )
162
+ end
163
+ end
164
+ nil
165
+ end
166
+
167
+ def fetch_realm(uri)
168
+ xml = fetch_resource( :realm, uri )
169
+ return DCloud::Realm.new( self, uri, xml ) if xml
170
+ nil
171
+ end
172
+
173
+ def images(opts={})
174
+ images = []
175
+ request_path = entry_points[:images]
176
+ request( request_path, :get, opts ) do |response|
177
+ doc = REXML::Document.new( response.body )
178
+ doc.get_elements( 'images/image' ).each do |image|
179
+ uri = image.attributes['href']
180
+ images << DCloud::Image.new( self, uri, image )
181
+ end
182
+ end
183
+ images
184
+ end
185
+
186
+ def image(id)
187
+ request( entry_points[:images], :get, {:id=>id } ) do |response|
188
+ doc = REXML::Document.new( response.body )
189
+ doc.get_elements( 'image' ).each do |instance|
190
+ uri = instance.attributes['href']
191
+ return DCloud::Image.new( self, uri, instance )
192
+ end
193
+ end
194
+ nil
195
+ end
196
+
197
+ def instances
198
+ instances = []
199
+ request( entry_points[:instances] ) do |response|
200
+ doc = REXML::Document.new( response.body )
201
+ doc.get_elements( 'instances/instance' ).each do |instance|
202
+ uri = instance.attributes['href']
203
+ instances << DCloud::Instance.new( self, uri, instance )
204
+ end
205
+ end
206
+ instances
207
+ end
208
+
209
+ def instance(id)
210
+ request( entry_points[:instances], :get, {:id=>id } ) do |response|
211
+ doc = REXML::Document.new( response.body )
212
+ doc.get_elements( 'instance' ).each do |instance|
213
+ uri = instance.attributes['href']
214
+ return DCloud::Instance.new( self, uri, instance )
215
+ end
216
+ end
217
+ nil
218
+ end
219
+
220
+ def post_instance(uri)
221
+ request( uri, :post ) do |response|
222
+ return true
223
+ end
224
+ return false
225
+ end
226
+
227
+ def fetch_instance(uri)
228
+ xml = fetch_resource( :instance, uri )
229
+ return DCloud::Instance.new( self, uri, xml ) if xml
230
+ nil
231
+ end
232
+
233
+ def create_instance(image_id, opts={})
234
+ name = opts[:name]
235
+ realm_id = opts[:realm]
236
+ flavor_id = opts[:flavor]
237
+
238
+ params = {}
239
+ ( params[:realm_id] = realm_id ) if realm_id
240
+ ( params[:flavor_id] = flavor_id ) if flavor_id
241
+ ( params[:name] = name ) if name
242
+
243
+ params[:image_id] = image_id
244
+ request( entry_points[:instances], :post, {}, params ) do |response|
245
+ doc = REXML::Document.new( response.body )
246
+ instance = doc.root
247
+ uri = instance.attributes['href']
248
+ return DCloud::Instance.new( self, uri, instance )
249
+ end
250
+ end
251
+
252
+ def storage_volumes
253
+ storage_volumes = []
254
+ request( entry_points[:storage_volumes] ) do |response|
255
+ doc = REXML::Document.new( response.body )
256
+ doc.get_elements( 'storage-volumes/storage-volume' ).each do |instance|
257
+ uri = instance.attributes['href']
258
+ storage_volumes << DCloud::StorageVolume.new( self, uri, instance )
259
+ end
260
+ end
261
+ storage_volumes
262
+ end
263
+
264
+ def storage_volume(id)
265
+ request( entry_points[:storage_volumes], :get, {:id=>id } ) do |response|
266
+ doc = REXML::Document.new( response.body )
267
+ doc.get_elements( 'storage-volume' ).each do |storage_volume|
268
+ uri = storage_volume.attributes['href']
269
+ return DCloud::StorageVolume.new( self, uri, storage_volume )
270
+ end
271
+ end
272
+ nil
273
+ end
274
+
275
+ def fetch_storage_volume(uri)
276
+ xml = fetch_resource( :storage_volume, uri )
277
+ return DCloud::StorageVolume.new( self, uri, xml ) if xml
278
+ nil
279
+ end
280
+
281
+ def storage_snapshots()
282
+ storage_snapshots = []
283
+ request( entry_points[:storage_snapshots] ) do |response|
284
+ doc = REXML::Document.new( response.body )
285
+ doc.get_elements( 'storage-snapshots/storage-snapshot' ).each do |instance|
286
+ uri = instance.attributes['href']
287
+ storage_snapshots << DCloud::StorageSnapshot.new( self, uri, instance )
288
+ end
289
+ end
290
+ storage_snapshots
291
+ end
292
+
293
+ def storage_snapshot(id)
294
+ request( entry_points[:storage_snapshots], :get, {:id=>id } ) do |response|
295
+ doc = REXML::Document.new( response.body )
296
+ doc.get_elements( 'storage-snapshot' ).each do |storage_snapshot|
297
+ uri = storage_snapshot.attributes['href']
298
+ return DCloud::StorageSnapshot.new( self, uri, storage_snapshot )
299
+ end
300
+ end
301
+ nil
302
+ end
303
+
304
+ def fetch_storage_snapshot(uri)
305
+ xml = fetch_resource( :storage_snapshot, uri )
306
+ return DCloud::StorageSnapshot.new( self, uri, xml ) if xml
307
+ nil
308
+ end
309
+
310
+ def fetch_image(uri)
311
+ xml = fetch_resource( :image, uri )
312
+ return DCloud::Image.new( self, uri, xml ) if xml
313
+ nil
314
+ end
315
+
316
+ private
317
+
318
+ attr_reader :http
319
+
320
+ def discover_entry_points
321
+ request(api_uri.to_s) do |response|
322
+ doc = REXML::Document.new( response.body )
323
+ @driver_name = doc.root.attributes['driver']
324
+ doc.get_elements( 'api/link' ).each do |link|
325
+ rel = link.attributes['rel']
326
+ uri = link.attributes['href']
327
+ @entry_points[rel.to_sym] = uri
328
+ end
329
+ end
330
+ end
331
+
332
+ def request(path='', method=:get, query_args={}, form_data={}, &block)
333
+ if ( path =~ /^http/ )
334
+ request_path = path
335
+ else
336
+ request_path = "#{api_uri.to_s}#{path}"
337
+ end
338
+ if query_args[:id]
339
+ request_path += "/#{query_args[:id]}"
340
+ query_args.delete(:id)
341
+ end
342
+ query_string = URI.escape(query_args.collect{|k,v| "#{k}=#{v}"}.join('&'))
343
+ request_path += "?#{query_string}" unless query_string==''
344
+ headers = {
345
+ :authorization => "Basic "+Base64.encode64("#{@name}:#{@password}"),
346
+ :accept => "application/xml"
347
+ }
348
+ logger << "Request [#{method.to_s.upcase}] #{request_path}]\n"
349
+ if method.eql?(:get)
350
+ RestClient.send(method, request_path, headers, &block)
351
+ else
352
+ RestClient.send(method, request_path, form_data, headers, &block)
353
+ end
354
+ end
355
+
356
+ end
@@ -0,0 +1,3 @@
1
+ :description: Fedora 10
2
+ :owner_id: fedoraproject
3
+ :architecture: x86_64