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.
- data/COPYING +502 -0
- data/Rakefile +39 -0
- data/credentials.yml +2 -0
- data/init.rb +19 -0
- data/lib/dcloud/base_model.rb +80 -0
- data/lib/dcloud/flavor.rb +43 -0
- data/lib/dcloud/image.rb +46 -0
- data/lib/dcloud/instance.rb +97 -0
- data/lib/dcloud/realm.rb +47 -0
- data/lib/dcloud/state.rb +30 -0
- data/lib/dcloud/storage_snapshot.rb +50 -0
- data/lib/dcloud/storage_volume.rb +53 -0
- data/lib/dcloud/transition.rb +35 -0
- data/lib/deltacloud.rb +356 -0
- data/specs/data/images/img1.yml +3 -0
- data/specs/data/images/img2.yml +3 -0
- data/specs/data/images/img3.yml +3 -0
- data/specs/data/instances/inst1.yml +8 -0
- data/specs/data/instances/inst2.yml +8 -0
- data/specs/data/storage_snapshots/snap1.yml +4 -0
- data/specs/data/storage_snapshots/snap2.yml +4 -0
- data/specs/data/storage_snapshots/snap3.yml +4 -0
- data/specs/data/storage_volumes/vol1.yml +6 -0
- data/specs/data/storage_volumes/vol2.yml +6 -0
- data/specs/data/storage_volumes/vol3.yml +6 -0
- data/specs/fixtures/images/img1.yml +3 -0
- data/specs/fixtures/images/img2.yml +3 -0
- data/specs/fixtures/images/img3.yml +3 -0
- data/specs/fixtures/instances/inst1.yml +8 -0
- data/specs/fixtures/instances/inst2.yml +8 -0
- data/specs/fixtures/storage_snapshots/snap1.yml +4 -0
- data/specs/fixtures/storage_snapshots/snap2.yml +4 -0
- data/specs/fixtures/storage_snapshots/snap3.yml +4 -0
- data/specs/fixtures/storage_volumes/vol1.yml +6 -0
- data/specs/fixtures/storage_volumes/vol2.yml +6 -0
- data/specs/fixtures/storage_volumes/vol3.yml +6 -0
- data/specs/flavors_spec.rb +67 -0
- data/specs/images_spec.rb +104 -0
- data/specs/initialization_spec.rb +59 -0
- data/specs/instance_states_spec.rb +77 -0
- data/specs/instances_spec.rb +171 -0
- data/specs/realms_spec.rb +65 -0
- data/specs/shared/resources.rb +29 -0
- data/specs/spec_helper.rb +54 -0
- data/specs/storage_snapshot_spec.rb +76 -0
- data/specs/storage_volume_spec.rb +86 -0
- 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
|