deltacloud-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/COPYING +502 -0
  2. data/Rakefile +108 -0
  3. data/bin/deltacloudd +88 -0
  4. data/config.ru +5 -0
  5. data/deltacloud.rb +14 -0
  6. data/lib/converters/xml_converter.rb +133 -0
  7. data/lib/deltacloud/base_driver.rb +19 -0
  8. data/lib/deltacloud/base_driver/base_driver.rb +189 -0
  9. data/lib/deltacloud/base_driver/features.rb +144 -0
  10. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +318 -0
  11. data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +170 -0
  12. data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +45 -0
  13. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +239 -0
  14. data/lib/deltacloud/drivers/mock/mock_driver.rb +275 -0
  15. data/lib/deltacloud/drivers/opennebula/cloud_client.rb +116 -0
  16. data/lib/deltacloud/drivers/opennebula/occi_client.rb +204 -0
  17. data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +241 -0
  18. data/lib/deltacloud/drivers/rackspace/rackspace_client.rb +129 -0
  19. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +150 -0
  20. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +254 -0
  21. data/lib/deltacloud/drivers/rimu/rimu_hosting_client.rb +87 -0
  22. data/lib/deltacloud/drivers/rimu/rimu_hosting_driver.rb +143 -0
  23. data/lib/deltacloud/hardware_profile.rb +131 -0
  24. data/lib/deltacloud/helpers.rb +5 -0
  25. data/lib/deltacloud/helpers/application_helper.rb +38 -0
  26. data/lib/deltacloud/helpers/conversion_helper.rb +39 -0
  27. data/lib/deltacloud/helpers/hardware_profiles_helper.rb +35 -0
  28. data/lib/deltacloud/models/base_model.rb +58 -0
  29. data/lib/deltacloud/models/image.rb +26 -0
  30. data/lib/deltacloud/models/instance.rb +37 -0
  31. data/lib/deltacloud/models/instance_profile.rb +47 -0
  32. data/lib/deltacloud/models/realm.rb +25 -0
  33. data/lib/deltacloud/models/storage_snapshot.rb +26 -0
  34. data/lib/deltacloud/models/storage_volume.rb +27 -0
  35. data/lib/deltacloud/state_machine.rb +84 -0
  36. data/lib/deltacloud/validation.rb +70 -0
  37. data/lib/drivers.rb +37 -0
  38. data/lib/sinatra/lazy_auth.rb +56 -0
  39. data/lib/sinatra/rabbit.rb +272 -0
  40. data/lib/sinatra/respond_to.rb +262 -0
  41. data/lib/sinatra/static_assets.rb +83 -0
  42. data/lib/sinatra/url_for.rb +44 -0
  43. data/public/favicon.ico +0 -0
  44. data/public/images/grid.png +0 -0
  45. data/public/images/logo-wide.png +0 -0
  46. data/public/images/rails.png +0 -0
  47. data/public/images/topbar-bg.png +0 -0
  48. data/public/javascripts/application.js +2 -0
  49. data/public/javascripts/controls.js +963 -0
  50. data/public/javascripts/dragdrop.js +973 -0
  51. data/public/javascripts/effects.js +1128 -0
  52. data/public/javascripts/prototype.js +4320 -0
  53. data/public/stylesheets/compiled/application.css +613 -0
  54. data/public/stylesheets/compiled/ie.css +31 -0
  55. data/public/stylesheets/compiled/print.css +27 -0
  56. data/public/stylesheets/compiled/screen.css +456 -0
  57. data/server.rb +340 -0
  58. data/tests/deltacloud_test.rb +60 -0
  59. data/tests/images_test.rb +94 -0
  60. data/tests/instances_test.rb +136 -0
  61. data/tests/realms_test.rb +56 -0
  62. data/tests/storage_snapshots_test.rb +48 -0
  63. data/tests/storage_volumes_test.rb +48 -0
  64. data/views/accounts/index.html.haml +11 -0
  65. data/views/accounts/show.html.haml +30 -0
  66. data/views/api/show.html.haml +15 -0
  67. data/views/api/show.xml.haml +5 -0
  68. data/views/docs/collection.html.haml +37 -0
  69. data/views/docs/collection.xml.haml +14 -0
  70. data/views/docs/index.html.haml +15 -0
  71. data/views/docs/index.xml.haml +5 -0
  72. data/views/docs/operation.html.haml +31 -0
  73. data/views/docs/operation.xml.haml +10 -0
  74. data/views/errors/auth_exception.html.haml +8 -0
  75. data/views/errors/auth_exception.xml.haml +2 -0
  76. data/views/errors/backend_error.html.haml +17 -0
  77. data/views/errors/backend_error.xml.haml +8 -0
  78. data/views/errors/validation_failure.html.haml +11 -0
  79. data/views/errors/validation_failure.xml.haml +7 -0
  80. data/views/hardware_profiles/index.html.haml +25 -0
  81. data/views/hardware_profiles/index.xml.haml +4 -0
  82. data/views/hardware_profiles/show.html.haml +19 -0
  83. data/views/hardware_profiles/show.xml.haml +17 -0
  84. data/views/images/index.html.haml +30 -0
  85. data/views/images/index.xml.haml +7 -0
  86. data/views/images/show.html.haml +21 -0
  87. data/views/images/show.xml.haml +5 -0
  88. data/views/instance_states/show.gv.erb +45 -0
  89. data/views/instance_states/show.html.haml +31 -0
  90. data/views/instance_states/show.xml.haml +8 -0
  91. data/views/instances/index.html.haml +29 -0
  92. data/views/instances/index.xml.haml +23 -0
  93. data/views/instances/new.html.haml +49 -0
  94. data/views/instances/show.html.haml +42 -0
  95. data/views/instances/show.xml.haml +28 -0
  96. data/views/layout.html.haml +23 -0
  97. data/views/realms/index.html.haml +29 -0
  98. data/views/realms/index.xml.haml +12 -0
  99. data/views/realms/show.html.haml +15 -0
  100. data/views/realms/show.xml.haml +10 -0
  101. data/views/root/index.html.haml +4 -0
  102. data/views/storage_snapshots/index.html.haml +20 -0
  103. data/views/storage_snapshots/index.xml.haml +11 -0
  104. data/views/storage_snapshots/show.html.haml +14 -0
  105. data/views/storage_snapshots/show.xml.haml +9 -0
  106. data/views/storage_volumes/index.html.haml +21 -0
  107. data/views/storage_volumes/index.xml.haml +13 -0
  108. data/views/storage_volumes/show.html.haml +20 -0
  109. data/views/storage_volumes/show.xml.haml +13 -0
  110. metadata +311 -0
data/server.rb ADDED
@@ -0,0 +1,340 @@
1
+ require 'rubygems'
2
+ require 'deltacloud'
3
+ require 'json'
4
+ require 'sinatra'
5
+ require 'sinatra/respond_to'
6
+ require 'erb'
7
+ require 'haml'
8
+ require 'open3'
9
+ require 'builder'
10
+ require 'drivers'
11
+ require 'sinatra/static_assets'
12
+ require 'sinatra/rabbit'
13
+ require 'sinatra/lazy_auth'
14
+ require 'deltacloud/validation'
15
+ require 'deltacloud/helpers'
16
+
17
+ configure do
18
+ set :raise_errors => false
19
+ end
20
+
21
+ configure :development do
22
+ # So we can just use puts for logging
23
+ $stdout.sync = true
24
+ $stderr.sync = true
25
+ end
26
+
27
+ DRIVER=ENV['API_DRIVER'] ? ENV['API_DRIVER'].to_sym : :mock
28
+
29
+ # You could use $API_HOST environment variable to change your hostname to
30
+ # whatever you want (eg. if you running API behind NAT)
31
+ HOSTNAME=ENV['API_HOST'] ? ENV['API_HOST'] : nil
32
+
33
+ Rack::Mime::MIME_TYPES.merge!({ ".gv" => "text/plain" })
34
+
35
+ Sinatra::Application.register Sinatra::RespondTo
36
+
37
+ # Common actions
38
+ #
39
+
40
+ def filter_all(model)
41
+ filter = {}
42
+ filter.merge!(:id => params[:id]) if params[:id]
43
+ filter.merge!(:architecture => params[:architecture]) if params[:architecture]
44
+ filter.merge!(:owner_id => params[:owner_id]) if params[:owner_id]
45
+ filter.merge!(:state => params[:state]) if params[:state]
46
+ filter = nil if filter.keys.size.eql?(0)
47
+ singular = model.to_s.singularize.to_sym
48
+ @elements = driver.send(model.to_sym, credentials, filter)
49
+ instance_variable_set(:"@#{model}", @elements)
50
+ respond_to do |format|
51
+ format.json { convert_to_json(singular, @elements) }
52
+ format.html { haml :"#{model}/index" }
53
+ format.xml { haml :"#{model}/index" }
54
+ end
55
+ end
56
+
57
+ def show(model)
58
+ @element = driver.send(model, credentials, { :id => params[:id]} )
59
+ instance_variable_set("@#{model}", @element)
60
+ respond_to do |format|
61
+ format.json { convert_to_json(model, @element) }
62
+ format.html { haml :"#{model.to_s.pluralize}/show" }
63
+ format.xml { haml :"#{model.to_s.pluralize}/show" }
64
+ end
65
+ end
66
+
67
+
68
+ #
69
+ # Error handlers
70
+ #
71
+ def report_error(status, template)
72
+ @error = request.env['sinatra.error']
73
+ response.status = status
74
+ respond_to do |format|
75
+ format.xml { haml :"errors/#{template}", :layout => false }
76
+ format.html { haml :"errors/#{template}" }
77
+ end
78
+ end
79
+
80
+ error Deltacloud::Validation::Failure do
81
+ report_error(400, "validation_failure")
82
+ end
83
+
84
+ error Deltacloud::AuthException do
85
+ report_error(403, "auth_exception")
86
+ end
87
+
88
+ error Deltacloud::BackendError do
89
+ report_error(500, "backend_error")
90
+ end
91
+
92
+ # Redirect to /api
93
+ get '/' do redirect '/api'; end
94
+
95
+ get '/api\/?' do
96
+ @version = 1.0
97
+ respond_to do |format|
98
+ format.xml { haml :"api/show" }
99
+ format.json do
100
+ { :api => {
101
+ :version => @version,
102
+ :driver => DRIVER,
103
+ :links => entry_points.collect { |l| { :rel => l[0], :href => l[1]} }
104
+ }
105
+ }.to_json
106
+ end
107
+ format.html { haml :"api/show" }
108
+ end
109
+ end
110
+
111
+ # Rabbit DSL
112
+
113
+ collection :realms do
114
+ description "Within a cloud provider a realm represents a boundary containing resources. The exact definition of a realm is left to the cloud provider. In some cases, a realm may represent different datacenters, different continents, or different pools of resources within a single datacenter. A cloud provider may insist that resources must all exist within a single realm in order to cooperate. For instance, storage volumes may only be allowed to be mounted to instances within the same realm."
115
+
116
+ operation :index do
117
+ description 'Operation will list all available realms. For specific architecture use "architecture" parameter.'
118
+ param :id, :string
119
+ param :architecture, :string, :optional, [ 'i386', 'x86_64' ]
120
+ control { filter_all(:realms) }
121
+ end
122
+
123
+ #FIXME: It always shows whole list
124
+ operation :show do
125
+ description 'Show an realm identified by "id" parameter.'
126
+ param :id, :string, :required
127
+ control { show(:realm) }
128
+ end
129
+
130
+ end
131
+
132
+ collection :images do
133
+ description "An image is a platonic form of a machine. Images are not directly executable, but are a template for creating actual instances of machines."
134
+
135
+ operation :index do
136
+ description 'The instances collection will return a set of all images available to the current use. You can filter images using "owner_id" and "architecture" parameter'
137
+ param :id, :string
138
+ param :owner_id, :string
139
+ param :architecture, :string, :optional
140
+ control { filter_all(:images) }
141
+ end
142
+
143
+ operation :show do
144
+ description 'Show an image identified by "id" parameter.'
145
+ param :id, :string, :required
146
+ control { show(:image) }
147
+ end
148
+
149
+ end
150
+
151
+ collection :instance_states do
152
+ description "The possible states of an instance, and how to traverse between them "
153
+
154
+ operation :index do
155
+ control do
156
+ @machine = driver.instance_state_machine
157
+ respond_to do |format|
158
+ format.xml { haml :'instance_states/show', :layout => false }
159
+ format.html { haml :'instance_states/show'}
160
+ format.gv { erb :"instance_states/show" }
161
+ format.png do
162
+ # Trick respond_to into looking up the right template for the
163
+ # graphviz file
164
+ format(:gv); gv = erb :"instance_states/show"; format(:png)
165
+ png = ''
166
+ cmd = 'dot -Kdot -Gpad="0.2,0.2" -Gsize="5.0,8.0" -Gdpi="180" -Tpng'
167
+ Open3.popen3( cmd ) do |stdin, stdout, stderr|
168
+ stdin.write( gv )
169
+ stdin.close()
170
+ png = stdout.read
171
+ end
172
+ png
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ # Special instance get operations that we only allow for HTML
180
+ get "/api/instances/:id/:action" do
181
+ meth = :"#{params[:action]}_instance"
182
+ not_found unless driver.respond_to?(meth)
183
+ respond_to do |format|
184
+ format.html do
185
+ driver.send(meth, credentials, params[:id])
186
+ if params[:action] == 'destroy'
187
+ redirect instances_url
188
+ else
189
+ redirect instance_url(params[:id])
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ get "/api/instances/new" do
196
+ @instance = Instance.new( { :id=>params[:id], :image_id=>params[:image_id] } )
197
+ @image = driver.image( credentials, :id => params[:image_id] )
198
+ @hardware_profiles = driver.hardware_profiles(credentials, :architecture => @image.architecture )
199
+ @realms = driver.realms(credentials)
200
+ respond_to do |format|
201
+ format.html { haml :"instances/new" }
202
+ end
203
+ end
204
+
205
+ def instance_action(name)
206
+ @instance = driver.send(:"#{name}_instance", credentials, params[:id])
207
+ respond_to do |format|
208
+ format.json {convert_to_json(:instance, @instance) }
209
+ format.html { haml :"instances/show" }
210
+ format.xml { haml :"instances/show" }
211
+ end
212
+ end
213
+
214
+ collection :instances do
215
+ description "An instance is a concrete machine realized from an image. The images collection may be obtained by following the link from the primary entry-point."
216
+
217
+ operation :index do
218
+ description "List all instances"
219
+ param :id, :string, :optional
220
+ param :state, :string, :optional
221
+ control { filter_all(:instances) }
222
+ end
223
+
224
+ operation :show do
225
+ description 'Show an image identified by "id" parameter.'
226
+ param :id, :string, :required
227
+ control { show(:instance) }
228
+ end
229
+
230
+ operation :create do
231
+ description "Create a new instance"
232
+ param :image_id, :string, :required
233
+ param :realm_id, :string, :optional
234
+ param :hwp_id, :string, :optional
235
+ control do
236
+ @image = driver.image(credentials, :id => params[:image_id])
237
+ instance = driver.create_instance(credentials, @image.id, params)
238
+ respond_to do |format|
239
+ format.xml do
240
+ response.status = 201 # Created
241
+ response['Location'] = instance_url(instance.id)
242
+ @instance = instance
243
+ haml :"instances/show"
244
+ end
245
+ format.html { redirect instance_url(instance.id) }
246
+ end
247
+ end
248
+ end
249
+
250
+ operation :reboot, :method => :post, :member => true do
251
+ description "Reboot running instance"
252
+ param :id, :string, :required
253
+ control { instance_action(:reboot) }
254
+ end
255
+
256
+ operation :start, :method => :post, :member => true do
257
+ description "Start an instance"
258
+ param :id, :string, :required
259
+ control { instance_action(:start) }
260
+ end
261
+
262
+ operation :stop, :method => :post, :member => true do
263
+ description "Stop running instance"
264
+ param :id, :string, :required
265
+ control { instance_action(:stop) }
266
+ end
267
+
268
+ operation :destroy do
269
+ description "Destroy instance"
270
+ param :id, :string, :required
271
+ control { instance_action(:destroy) }
272
+ end
273
+ end
274
+
275
+ collection :hardware_profiles do
276
+ description <<END
277
+ A hardware profile represents a configuration of resources upon which a
278
+ machine may be deployed. It defines aspects such as local disk storage,
279
+ available RAM, and architecture. Each provider is free to define as many
280
+ (or as few) hardware profiles as desired.
281
+ END
282
+
283
+ operation :index do
284
+ description "List of available hardware profiles"
285
+ param :id, :string
286
+ param :architecture, :string, :optional, [ 'i386', 'x86_64' ]
287
+ control do
288
+ @profiles = driver.hardware_profiles(credentials, params)
289
+ respond_to do |format|
290
+ format.xml { haml :'hardware_profiles/index' }
291
+ format.html { haml :'hardware_profiles/index' }
292
+ end
293
+ end
294
+ end
295
+
296
+ operation :show do
297
+ description "Show specific hardware profile"
298
+ param :id, :string, :required
299
+ control do
300
+ @profile = driver.hardware_profile(credentials, params[:id])
301
+ respond_to do |format|
302
+ format.xml { haml :'hardware_profiles/show', :layout => false }
303
+ format.html { haml :'hardware_profiles/show' }
304
+ end
305
+ end
306
+ end
307
+
308
+ end
309
+
310
+ collection :storage_snapshots do
311
+ description "Storage snapshots description here"
312
+
313
+ operation :index do
314
+ description "Listing of storage snapshots"
315
+ param :id, :string
316
+ control { filter_all(:storage_snapshots) }
317
+ end
318
+
319
+ operation :show do
320
+ description "Show storage snapshot"
321
+ param :id, :string, :required
322
+ control { show(:storage_snapshot) }
323
+ end
324
+ end
325
+
326
+ collection :storage_volumes do
327
+ description "Storage volumes description here"
328
+
329
+ operation :index do
330
+ description "Listing of storage volumes"
331
+ param :id, :string
332
+ control { filter_all(:storage_volumes) }
333
+ end
334
+
335
+ operation :show do
336
+ description "Show storage volume"
337
+ param :id, :string, :required
338
+ control { show(:storage_volume) }
339
+ end
340
+ end
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'rack/test'
4
+ require 'base64'
5
+ require 'nokogiri'
6
+ require 'pp'
7
+ require 'sinatra'
8
+
9
+ ENV['API_DRIVER']='mock'
10
+ ENV['API_HOST']='localhost'
11
+
12
+ require 'server'
13
+
14
+ set :environment => :test
15
+
16
+ module DeltacloudTest
17
+ include Rack::Test::Methods
18
+
19
+ def app
20
+ Sinatra::Application
21
+ end
22
+
23
+ def test_if_response_is_valid
24
+ get '/api/'+@collection+'.xml', @params, rack_headers
25
+ assert last_response.ok?
26
+ end
27
+
28
+ def test_if_http_status_is_correct_with_wrong_credentials
29
+ return if ['realms'].include?(@collection)
30
+ wrong_header = rack_headers
31
+ wrong_header['HTTP_AUTHORIZATION'] = authorization('wronguser', 'wrongpassword')
32
+ get '/api/'+@collection+'.xml', @params, wrong_header
33
+ assert_equal 403, last_response.status
34
+ end
35
+
36
+ def test_if_index_operation_proper_root_element
37
+ get '/api/'+@collection+'.xml', @params, rack_headers
38
+ doc = Nokogiri::XML.parse(last_response.body)
39
+ assert_equal @collection.gsub('_', '-'), doc.root.name
40
+ end
41
+
42
+ def test_html_response
43
+ get '/api/'+@collection+'.html', @params, rack_headers
44
+ doc = Nokogiri::HTML.parse(last_response.body)
45
+ assert_equal 'html', doc.root.name
46
+ end
47
+
48
+ def authorization(username, password)
49
+ "Basic " + Base64.encode64("#{username}:#{password}")
50
+ end
51
+
52
+ def rack_headers
53
+ return {
54
+ 'HTTP_AUTHORIZATION' => authorization('mockuser', 'mockpassword'),
55
+ 'SERVER_PORT' => '4040',
56
+ 'Accept' => 'application/xml;q=1'
57
+ }
58
+ end
59
+
60
+ end
@@ -0,0 +1,94 @@
1
+ require 'tests/deltacloud_test'
2
+
3
+ class ImagesTest < Test::Unit::TestCase
4
+
5
+ def initialize(*args)
6
+ @collection = 'images'
7
+ @operations = [:index, :show]
8
+ @params = {}
9
+ super(*args)
10
+ end
11
+
12
+ def test_if_images_are_not_empty
13
+ get '/api/images.xml', @params, rack_headers
14
+ doc = Nokogiri::XML.parse(last_response.body)
15
+ assert_not_equal 0, doc.xpath('/images/image').size
16
+ end
17
+
18
+ [:id, :owner_id, :name, :description, :architecture].each do |option|
19
+ method_name = :"test_if_images_index_contain_#{option}"
20
+ send :define_method, method_name do
21
+ get '/api/images.xml', @params, rack_headers
22
+ doc = Nokogiri::XML.parse(last_response.body)
23
+ elt = doc.xpath('/images/image[1]').first
24
+ assert_not_nil elt.xpath(option.to_s).first
25
+ end
26
+ end
27
+
28
+ [:id, :owner_id, :name, :description, :architecture].each do |option|
29
+ method_name = :"test_if_image_show_contain_#{option}"
30
+ send :define_method, method_name do
31
+ get '/api/images/img1.xml', @params, rack_headers
32
+ doc = Nokogiri::XML.parse(last_response.body)
33
+ elt = doc.xpath('/image').first
34
+ assert_not_nil elt.xpath(option.to_s).first
35
+ end
36
+ end
37
+
38
+ def test_images_filtering_by_id
39
+ @params={ :id => 'img1' }
40
+ get '/api/images.xml', @params, rack_headers
41
+ doc = Nokogiri::XML.parse(last_response.body)
42
+ assert_equal 1, doc.xpath('/images/image').size
43
+ assert_equal @params[:id], doc.xpath('/images/image/id').first.text
44
+ end
45
+
46
+ def test_images_filtering_by_owner_id
47
+ @params={ :owner_id => 'fedoraproject' }
48
+ get '/api/images.xml', @params, rack_headers
49
+ doc = Nokogiri::XML.parse(last_response.body)
50
+ assert_equal 2, doc.xpath('/images/image').size
51
+ assert_equal @params[:owner_id], doc.xpath('/images/image/owner_id')[0].text
52
+ assert_equal @params[:owner_id], doc.xpath('/images/image/owner_id')[1].text
53
+ end
54
+
55
+ def test_images_filtering_by_architecture
56
+ @params={ :architecture => 'i386' }
57
+ get '/api/images.xml', @params, rack_headers
58
+ doc = Nokogiri::XML.parse(last_response.body)
59
+ assert_equal 2, doc.xpath('/images/image').size
60
+ assert_equal @params[:architecture], doc.xpath('/images/image/architecture')[0].text
61
+ assert_equal @params[:architecture], doc.xpath('/images/image/architecture')[1].text
62
+ end
63
+
64
+ def test_images_filtering_by_id_and_owner_id
65
+ @params={ :id => 'img1', :owner_id => 'fedoraproject' }
66
+ get '/api/images.xml', @params, rack_headers
67
+ doc = Nokogiri::XML.parse(last_response.body)
68
+ assert_equal 1, doc.xpath('/images/image').size
69
+ assert_equal @params[:owner_id], doc.xpath('/images/image/owner_id')[0].text
70
+ assert_equal @params[:id], doc.xpath('/images/image/id')[0].text
71
+ end
72
+
73
+ def test_images_filtering_by_id_and_owner_id_and_architecture
74
+ @params={ :id => 'img1', :owner_id => 'fedoraproject', :architecture => 'x86_64' }
75
+ get '/api/images.xml', @params, rack_headers
76
+ doc = Nokogiri::XML.parse(last_response.body)
77
+ assert_equal 1, doc.xpath('/images/image').size
78
+ assert_equal @params[:owner_id], doc.xpath('/images/image/owner_id')[0].text
79
+ assert_equal @params[:id], doc.xpath('/images/image/id')[0].text
80
+ assert_equal @params[:architecture], doc.xpath('/images/image/architecture')[0].text
81
+ end
82
+
83
+ def test_images_filtering_by_id_and_architecture
84
+ @params={ :id => 'img1', :architecture => 'x86_64' }
85
+ get '/api/images.xml', @params, rack_headers
86
+ doc = Nokogiri::XML.parse(last_response.body)
87
+ assert_equal 1, doc.xpath('/images/image').size
88
+ assert_equal @params[:id], doc.xpath('/images/image/id')[0].text
89
+ assert_equal @params[:architecture], doc.xpath('/images/image/architecture')[0].text
90
+ end
91
+
92
+ include DeltacloudTest
93
+
94
+ end