deltacloud-core 0.0.1

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 (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