deltacloud-core 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. data/Rakefile +63 -7
  2. data/bin/deltacloudd +29 -17
  3. data/config.ru +5 -3
  4. data/config/drivers/ec2.yaml +9 -0
  5. data/config/drivers/google.yaml +3 -0
  6. data/config/drivers/openstack.yaml +3 -0
  7. data/deltacloud-core.gemspec +1 -1
  8. data/lib/cimi/dependencies.rb +62 -0
  9. data/lib/cimi/helpers/cimi_helper.rb +50 -0
  10. data/lib/cimi/model.rb +52 -0
  11. data/lib/cimi/model/action.rb +24 -0
  12. data/lib/cimi/model/base.rb +249 -0
  13. data/lib/cimi/model/cloud_entry_point.rb +48 -0
  14. data/lib/cimi/model/entity_metadata.rb +83 -0
  15. data/lib/cimi/model/entity_metadata_collection.rb +31 -0
  16. data/lib/cimi/model/errors.rb +40 -0
  17. data/lib/cimi/model/machine.rb +227 -0
  18. data/lib/cimi/model/machine_admin.rb +59 -0
  19. data/lib/cimi/model/machine_admin_collection.rb +34 -0
  20. data/lib/cimi/model/machine_collection.rb +34 -0
  21. data/lib/cimi/model/machine_configuration.rb +67 -0
  22. data/lib/cimi/model/machine_configuration_collection.rb +34 -0
  23. data/lib/cimi/model/machine_image.rb +46 -0
  24. data/lib/cimi/model/machine_image_collection.rb +34 -0
  25. data/lib/cimi/model/machine_template.rb +41 -0
  26. data/lib/cimi/model/machine_template_collection.rb +34 -0
  27. data/lib/cimi/model/network.rb +69 -0
  28. data/lib/cimi/model/network_collection.rb +34 -0
  29. data/lib/cimi/model/network_configuration.rb +50 -0
  30. data/lib/cimi/model/network_configuration_collection.rb +34 -0
  31. data/lib/cimi/model/network_template.rb +26 -0
  32. data/lib/cimi/model/schema.rb +277 -0
  33. data/lib/cimi/model/volume.rb +103 -0
  34. data/lib/cimi/model/volume_collection.rb +34 -0
  35. data/lib/cimi/model/volume_configuration.rb +60 -0
  36. data/lib/cimi/model/volume_configuration_collection.rb +34 -0
  37. data/lib/cimi/model/volume_image.rb +49 -0
  38. data/lib/cimi/model/volume_image_collection.rb +34 -0
  39. data/lib/cimi/model/volume_template.rb +23 -0
  40. data/lib/cimi/model/volume_template_collection.rb +34 -0
  41. data/lib/cimi/server.rb +575 -0
  42. data/lib/deltacloud/base_driver/base_driver.rb +11 -1
  43. data/lib/deltacloud/core_ext.rb +2 -0
  44. data/lib/deltacloud/core_ext/array.rb +25 -0
  45. data/lib/deltacloud/core_ext/hash.rb +7 -0
  46. data/lib/deltacloud/core_ext/proc.rb +31 -0
  47. data/lib/deltacloud/core_ext/string.rb +15 -0
  48. data/lib/deltacloud/drivers/condor/condor_driver.rb +2 -1
  49. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +32 -10
  50. data/lib/deltacloud/drivers/eucalyptus/eucalyptus_driver.rb +1 -1
  51. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +1 -1
  52. data/lib/deltacloud/drivers/google/google_driver.rb +233 -0
  53. data/lib/deltacloud/drivers/mock/data/instances/inst0.yml +7 -2
  54. data/lib/deltacloud/drivers/mock/data/instances/inst1.yml +7 -2
  55. data/lib/deltacloud/drivers/mock/data/instances/inst2.yml +7 -2
  56. data/lib/deltacloud/drivers/mock/mock_client.rb +17 -0
  57. data/lib/deltacloud/drivers/mock/mock_driver.rb +82 -8
  58. data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +1 -1
  59. data/lib/deltacloud/drivers/openstack/openstack_driver.rb +47 -0
  60. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +8 -8
  61. data/lib/deltacloud/drivers/rhevm/rhevm_client.rb +122 -49
  62. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +42 -22
  63. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +1 -1
  64. data/lib/deltacloud/drivers/sbc/sbc_driver.rb +3 -2
  65. data/lib/deltacloud/drivers/terremark/terremark_driver.rb +2 -2
  66. data/lib/deltacloud/drivers/vsphere/vsphere_client.rb +25 -4
  67. data/lib/deltacloud/drivers/vsphere/vsphere_driver.rb +35 -12
  68. data/lib/deltacloud/hardware_profile.rb +34 -10
  69. data/lib/deltacloud/helpers/application_helper.rb +3 -28
  70. data/lib/deltacloud/helpers/blob_stream.rb +2 -1
  71. data/lib/deltacloud/models.rb +2 -0
  72. data/lib/deltacloud/models/bucket.rb +1 -1
  73. data/lib/deltacloud/models/image.rb +1 -1
  74. data/lib/deltacloud/models/instance.rb +2 -1
  75. data/lib/deltacloud/models/instance_address.rb +56 -0
  76. data/lib/deltacloud/models/provider.rb +27 -0
  77. data/{server.rb → lib/deltacloud/server.rb} +72 -14
  78. data/lib/deltacloud/validation.rb +31 -10
  79. data/lib/sinatra/rabbit.rb +34 -26
  80. data/lib/sinatra/rack_accept.rb +5 -5
  81. data/lib/sinatra/rack_matrix_params.rb +6 -2
  82. data/lib/sinatra/rack_syslog.rb +3 -3
  83. data/lib/sinatra/static_assets.rb +1 -1
  84. data/lib/sinatra/url_for.rb +1 -7
  85. data/public/images/bread-bg.png +0 -0
  86. data/public/images/logo-wide.png +0 -0
  87. data/public/images/topbar-bg.png +0 -0
  88. data/public/javascripts/application.js +5 -0
  89. data/public/javascripts/cmwgapp.js +249 -0
  90. data/public/javascripts/jquery-1.4.2.min.js +154 -0
  91. data/public/javascripts/jquery.mobile-1.0rc1.min.js +170 -0
  92. data/public/stylesheets/images/icons-18-black.png +0 -0
  93. data/public/stylesheets/images/icons-18-white.png +0 -0
  94. data/public/stylesheets/images/icons-36-black.png +0 -0
  95. data/public/stylesheets/images/icons-36-white.png +0 -0
  96. data/public/stylesheets/jquery.mobile-1.0rc1.min.css +12 -0
  97. data/public/stylesheets/new.css +4 -0
  98. data/support/fedora/deltacloud-core.init +20 -13
  99. data/tests/cimi/features/step_definitions/common_steps.rb +59 -0
  100. data/tests/cimi/features/step_definitions/machine_images_steps.rb +0 -0
  101. data/tests/cimi/features/step_definitions/machines_steps.rb +99 -0
  102. data/tests/cimi/features/step_definitions/volumes_steps.rb +115 -0
  103. data/tests/cimi/features/support/env.rb +53 -0
  104. data/tests/common.rb +89 -11
  105. data/tests/core_ext/string.rb +31 -0
  106. data/tests/drivers/google/api_test.rb +35 -0
  107. data/tests/drivers/google/buckets_test.rb +116 -0
  108. data/tests/drivers/google/setup.rb +38 -0
  109. data/tests/drivers/mock/instances_test.rb +20 -5
  110. data/tests/drivers/openstack/api_test.rb +41 -0
  111. data/tests/drivers/openstack/hardware_profiles_test.rb +53 -0
  112. data/tests/drivers/openstack/images_test.rb +40 -0
  113. data/tests/drivers/openstack/instances_test.rb +163 -0
  114. data/tests/drivers/openstack/realms_test.rb +36 -0
  115. data/tests/drivers/openstack/setup.rb +20 -0
  116. data/tests/drivers/rackspace/buckets_test.rb +145 -0
  117. data/tests/drivers/rackspace/setup.rb +3 -3
  118. data/tests/drivers/rhevm/api_test.rb +1 -1
  119. data/tests/drivers/rhevm/images_test.rb +2 -2
  120. data/tests/drivers/rhevm/instances_test.rb +10 -12
  121. data/tests/drivers/rhevm/realms_test.rb +4 -4
  122. data/tests/drivers/rhevm/setup.rb +3 -3
  123. data/tests/rabbit_test.rb +1 -1
  124. data/views/api/show.html.haml +13 -0
  125. data/views/cimi/cloudEntryPoint/index.html.haml +5 -0
  126. data/views/cimi/cloudEntryPoint/index.xml.haml +9 -0
  127. data/views/cimi/collection/index.html.haml +45 -0
  128. data/views/cimi/collection/response.xml.haml +3 -0
  129. data/views/cimi/error.html.haml +31 -0
  130. data/views/cimi/errors/400.html.haml +41 -0
  131. data/views/cimi/errors/400.xml.haml +3 -0
  132. data/views/cimi/errors/401.html.haml +41 -0
  133. data/views/cimi/errors/401.xml.haml +2 -0
  134. data/views/cimi/errors/403.html.haml +42 -0
  135. data/views/cimi/errors/403.xml.haml +2 -0
  136. data/views/cimi/errors/404.html.haml +29 -0
  137. data/views/cimi/errors/404.xml.haml +2 -0
  138. data/views/cimi/errors/405.html.haml +29 -0
  139. data/views/cimi/errors/405.xml.haml +5 -0
  140. data/views/cimi/errors/500.html.haml +43 -0
  141. data/views/cimi/errors/500.xml.haml +6 -0
  142. data/views/cimi/errors/502.html.haml +43 -0
  143. data/views/cimi/errors/502.xml.haml +7 -0
  144. data/views/cimi/errors/backend_capability_failure.html.haml +29 -0
  145. data/views/cimi/layout.html.haml +32 -0
  146. data/views/cimi/machine_configurations/show.html.haml +159 -0
  147. data/views/cimi/machine_configurations/show.xml.haml +27 -0
  148. data/views/cimi/machine_images/show.html.haml +79 -0
  149. data/views/cimi/machine_images/show.xml.haml +17 -0
  150. data/views/cimi/machines/show.html.haml +177 -0
  151. data/views/cimi/machines/show.xml.haml +28 -0
  152. data/views/cimi/volumes/show.html.haml +68 -0
  153. data/views/cimi/volumes/show.xml.haml +17 -0
  154. data/views/drivers/show.html.haml +10 -5
  155. data/views/drivers/show.xml.haml +9 -4
  156. data/views/error.html.haml +2 -2
  157. data/views/errors/500.xml.haml +7 -1
  158. data/views/instances/index.html.haml +1 -1
  159. data/views/instances/new.html.haml +19 -16
  160. data/views/instances/show.html.haml +7 -2
  161. data/views/instances/show.xml.haml +8 -7
  162. data/views/layout.html.haml +2 -2
  163. data/views/storage_volumes/show.html.haml +1 -1
  164. metadata +296 -204
  165. data/public/javascripts/jquery.mobile-1.0b1.min.js +0 -146
  166. data/public/stylesheets/jquery.mobile-1.0b1.min.css +0 -8
@@ -20,6 +20,7 @@ require 'deltacloud/models/image'
20
20
  require 'deltacloud/models/instance'
21
21
  require 'deltacloud/models/key'
22
22
  require 'deltacloud/models/address'
23
+ require 'deltacloud/models/instance_address'
23
24
  require 'deltacloud/models/instance_profile'
24
25
  require 'deltacloud/models/storage_snapshot'
25
26
  require 'deltacloud/models/storage_volume'
@@ -28,3 +29,4 @@ require 'deltacloud/models/blob'
28
29
  require 'deltacloud/models/load_balancer'
29
30
  require 'deltacloud/models/firewall'
30
31
  require 'deltacloud/models/firewall_rule'
32
+ require 'deltacloud/models/provider'
@@ -26,7 +26,7 @@ class Bucket < BaseModel
26
26
  h = self.to_hash_original
27
27
  unless blob_list.nil?
28
28
  h[:blob_list] = self.blob_list.collect { |blob| { :id => blob,
29
- :href => "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/#{self.id}/#{blob}"}}
29
+ :href => "#{settings.root_url}/buckets/#{self.id}/#{blob}"}}
30
30
  end
31
31
  return h
32
32
  end
@@ -30,7 +30,7 @@ class Image < BaseModel
30
30
  h.merge({
31
31
  :actions => [ :create_instance => {
32
32
  :method => 'post',
33
- :href => "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/instances;image_id=#{self.id}"
33
+ :href => "#{settings.root_url}/instances;image_id=#{self.id}"
34
34
  }]
35
35
  })
36
36
  end
@@ -34,6 +34,7 @@ class Instance < BaseModel
34
34
  attr_accessor :password
35
35
  attr_accessor :create_image
36
36
  attr_accessor :firewalls
37
+ attr_accessor :storage_volumes
37
38
 
38
39
  def can_create_image?
39
40
  self.create_image
@@ -75,7 +76,7 @@ class Instance < BaseModel
75
76
  def to_hash
76
77
  h = self.to_hash_original
77
78
  h[:public_addresses] = h[:public_addresses].collect do |address|
78
- { :address => { :type => address_type(address), :value => address } }
79
+ { :address => { :type => address.address_type, :value => address } }
79
80
  end
80
81
  h[:actions] = self.actions.collect do |action|
81
82
  { :"#{action}" => {
@@ -0,0 +1,56 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership. The
4
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations
14
+ # under the License.
15
+
16
+ # Model to store the hardware profile applied to an instance together with
17
+ # any instance-specific overrides
18
+
19
+ class InstanceAddress
20
+ attr_accessor :address
21
+ attr_accessor :port
22
+ attr_accessor :address_type
23
+
24
+ def initialize(address, opts={})
25
+ self.address = address
26
+ self.port = opts[:port] if opts[:port]
27
+ self.address_type = opts[:type] || :ipv4
28
+ self
29
+ end
30
+
31
+ def address_type
32
+ (address and !address.strip.empty?) ? @address_type : :unavailable
33
+ end
34
+
35
+ def to_s
36
+ return ['VNC', address, port].join(':') if is_vnc?
37
+ address
38
+ end
39
+
40
+ def is_mac?
41
+ address_type == :mac
42
+ end
43
+
44
+ def is_ipv4?
45
+ address_type == :ipv4
46
+ end
47
+
48
+ def is_hostname?
49
+ address_type == :hostname
50
+ end
51
+
52
+ def is_vnc?
53
+ address_type == :vnc
54
+ end
55
+
56
+ end
@@ -0,0 +1,27 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership. The
4
+ # ASF licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations
14
+ # under the License.
15
+
16
+ # Model to store the hardware profile applied to an instance together with
17
+ # any instance-specific overrides
18
+
19
+ class Provider < BaseModel
20
+ attr_accessor :url
21
+ attr_accessor :name
22
+
23
+ def initialize(opts={})
24
+ super(opts)
25
+ end
26
+
27
+ end
@@ -31,7 +31,7 @@ require 'sinatra/rack_date'
31
31
  require 'sinatra/rack_matrix_params'
32
32
  require 'sinatra/rack_syslog'
33
33
 
34
- set :version, '0.4.1'
34
+ set :version, '0.5.0'
35
35
 
36
36
  include Deltacloud::Drivers
37
37
  set :drivers, Proc.new { driver_config }
@@ -46,8 +46,15 @@ use Rack::MediaType
46
46
  use Rack::Date
47
47
 
48
48
  configure do
49
- set :views, File.dirname(__FILE__) + '/views'
50
- set :public_folder, File.dirname(__FILE__) + '/public'
49
+ set :root_url, "/api"
50
+ set :views, File::join($top_srcdir, 'views')
51
+ # NOTE: Change :public to :public_folder once we update sinatra to 1.3
52
+ # set :public_folder, File::join($top_srcdir, 'public')
53
+ if settings.respond_to? :public_folder
54
+ set :public_folder, File::join($top_srcdir, 'public')
55
+ else
56
+ set :public, File::join($top_srcdir, 'public')
57
+ end
51
58
  # Try to load the driver on startup to fail early if there are issues
52
59
  driver
53
60
  end
@@ -87,13 +94,24 @@ after do
87
94
  end
88
95
 
89
96
  # Redirect to /api
90
- get '/' do redirect root_url, 301; end
97
+ get '/' do redirect settings.root_url, 301; end
98
+
99
+
100
+ # Generate a root route for API docs
101
+ get "#{settings.root_url}/docs\/?" do
102
+ respond_to do |format|
103
+ format.html { haml :'docs/index' }
104
+ format.xml { haml :'docs/index' }
105
+ end
106
+ end
91
107
 
92
- get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}\/?" do
108
+ get "#{settings.root_url}\/?" do
93
109
  if params[:force_auth]
94
110
  return [401, 'Authentication failed'] unless driver.valid_credentials?(credentials)
95
111
  end
96
112
  @collections = [:drivers] + driver.supported_collections
113
+ @driver_name = driver.name unless driver.name.to_sym == DRIVER
114
+ @providers = driver.configured_providers
97
115
  respond_to do |format|
98
116
  format.xml { haml :"api/show" }
99
117
  format.json do
@@ -110,6 +128,15 @@ get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}\/?" do
110
128
  end
111
129
  end
112
130
 
131
+ post "#{settings.root_url}\/?" do
132
+ p = {}
133
+ ["provider", "driver"].each { |k| p[k] = params[k] if params[k] }
134
+ p.delete("provider") if p["provider"] == "default"
135
+ q = p.map { |k,v| "#{k}=#{v}" }.join(";")
136
+ q = ";" + q unless q.empty?
137
+ redirect "#{settings.root_url}#{q}", 301
138
+ end
139
+
113
140
  # Rabbit DSL
114
141
 
115
142
  collection :drivers do
@@ -136,6 +163,9 @@ EOS
136
163
  param :id, :string
137
164
  control do
138
165
  @name = params[:id].to_sym
166
+ if driver_symbol == @name
167
+ @providers = driver.providers(credentials) if driver.respond_to? :providers
168
+ end
139
169
  @driver = settings.drivers[@name]
140
170
  return [404, "Driver #{@name} not found"] unless @driver
141
171
  respond_to do |format|
@@ -228,7 +258,7 @@ END
228
258
  :description => params[:description]
229
259
  })
230
260
  status 201 # Created
231
- response['Location'] = image_url(@instance.id)
261
+ response['Location'] = image_url(@image.id)
232
262
  respond_to do |format|
233
263
  format.xml { haml :"images/show" }
234
264
  format.json { convert_to_json(:image, @image) }
@@ -293,7 +323,7 @@ collection :instance_states do
293
323
  end
294
324
  end
295
325
 
296
- get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/instances/:id/run" do
326
+ get "#{settings.root_url}/instances/:id/run" do
297
327
  @instance = driver.instance(credentials, :id => params[:id])
298
328
  respond_to do |format|
299
329
  format.html { haml :"instances/run_command" }
@@ -448,14 +478,32 @@ END
448
478
  param :hwp_id, :string, :optional
449
479
  control do
450
480
  @instance = driver.create_instance(credentials, params[:image_id], params)
481
+ if @instance.kind_of? Array
482
+ @elements = @instance
483
+ action_handler = "index"
484
+ else
485
+ response['Location'] = instance_url(@instance.id)
486
+ action_handler = "show"
487
+ end
451
488
  status 201 # Created
452
- response['Location'] = instance_url(@instance.id)
453
489
  respond_to do |format|
454
- format.xml { haml :"instances/show" }
455
- format.json { convert_to_json(:instance, @instance) }
490
+ format.xml { haml :"instances/#{action_handler}" }
491
+ format.json do
492
+ if @elements
493
+ convert_to_json(:instances, @elements)
494
+ else
495
+ convert_to_json(:instance, @elements)
496
+ end
497
+ end
456
498
  format.html do
457
- redirect instance_url(@instance.id) if @instance and @instance.id
458
- redirect instances_url
499
+ if @elements
500
+ haml :"instances/index"
501
+ elsif @instance and @instance.id
502
+ response['Location'] = instance_url(@instance.id)
503
+ redirect instance_url(@instance.id)
504
+ else
505
+ redirect instances_url
506
+ end
459
507
  end
460
508
  end
461
509
  end
@@ -651,6 +699,16 @@ collection :storage_volumes do
651
699
  end
652
700
  end
653
701
 
702
+ operation :attach_instance, :method=>:get, :member=>true do
703
+ description "A form to attach a storage volume to an instance"
704
+ control do
705
+ @instances = driver.instances(credentials)
706
+ respond_to do |format|
707
+ format.html{ haml :"storage_volumes/attach"}
708
+ end
709
+ end
710
+ end
711
+
654
712
  operation :attach, :method => :post, :member => true do
655
713
  description "Attach storage volume to instance"
656
714
  with_capability :attach_storage_volume
@@ -769,7 +827,7 @@ end
769
827
  # existing blob
770
828
  NEW_BLOB_FORM_ID = "new_blob_form_d15cfd90"
771
829
 
772
- get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/:bucket/#{NEW_BLOB_FORM_ID}" do
830
+ get "#{settings.root_url}/buckets/:bucket/#{NEW_BLOB_FORM_ID}" do
773
831
  @bucket_id = params[:bucket]
774
832
  respond_to do |format|
775
833
  format.html {haml :"blobs/new"}
@@ -992,7 +1050,7 @@ collection :buckets do
992
1050
 
993
1051
  end
994
1052
 
995
- get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/addresses/:id/associate" do
1053
+ get "#{settings.root_url}/addresses/:id/associate" do
996
1054
  @instances = driver.instances(credentials)
997
1055
  @address = Address::new(:id => params[:id])
998
1056
  respond_to do |format|
@@ -16,13 +16,12 @@
16
16
 
17
17
  module Deltacloud::Validation
18
18
 
19
- class Failure < StandardError
19
+ class Failure < Deltacloud::ExceptionHandler::DeltacloudException
20
20
  attr_reader :param
21
- def initialize(param, msg='')
22
- super(msg)
23
- @param = param
21
+ def initialize(e, message=nil)
22
+ message ||= e.message
23
+ super(400, e.class.name, message, [])
24
24
  end
25
-
26
25
  def name
27
26
  param.name if @param
28
27
  end
@@ -46,6 +45,19 @@ module Deltacloud::Validation
46
45
  def optional?
47
46
  type.eql?(:optional)
48
47
  end
48
+
49
+ def valid_value?(value)
50
+ true if (options.kind_of?(Range) or options.kind_of?(Array)) and options.include?(value)
51
+ true if options.kind_of?(String) and not options.empty?
52
+ end
53
+
54
+ def valid_hwp_value?(profile, value)
55
+ profile.property(@name.to_s.gsub(/^hwp_/, '')).valid?(value)
56
+ end
57
+
58
+ def hwp_property?
59
+ true if name.to_s =~ /^hwp_(cpu|memory|storage|architecture)/
60
+ end
49
61
  end
50
62
 
51
63
  def param(*args)
@@ -72,15 +84,24 @@ module Deltacloud::Validation
72
84
  params.each_value { |p| yield p }
73
85
  end
74
86
 
75
- def validate(all_params, values)
76
- all_params.each_value do |p|
87
+ def validate(current_driver, all_params, values, credentials)
88
+ all_params.each do |key, p|
77
89
  if p.required? and not values[p.name]
78
90
  raise Failure.new(p, "Required parameter #{p.name} not found")
79
91
  end
80
- if values[p.name] and not p.options.empty? and
81
- not p.options.include?(values[p.name])
82
- raise Failure.new(p, "Parameter #{p.name} has value #{values[p.name]} which is not in #{p.options.join(", ")}")
92
+ next unless values[p.name]
93
+ if p.hwp_property?
94
+ profile = current_driver.hardware_profile(credentials, values['hwp_id'])
95
+ raise Failure.new(p, "Unknown hardware profile selected #{values['hwp_id']}") unless profile
96
+ unless p.valid_hwp_value?(profile, values[p.name])
97
+ raise Failure.new(p, "Hardware profile property #{p.name} has invalid value #{values[p.name]}")
98
+ end
99
+ else
100
+ if not p.options.empty? and p.valid_value?(values[p.name])
101
+ raise Failure.new(p, "Parameter #{p.name} has value #{values[p.name]} which is not in #{p.options.join(", ")}")
102
+ end
83
103
  end
84
104
  end
85
105
  end
106
+
86
107
  end
@@ -89,8 +89,8 @@ module Sinatra
89
89
 
90
90
  def generate_documentation
91
91
  coll, oper = @collection, self
92
- Rabbit::routes << [:get, "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/docs/#{@collection.name}/#{@name}"]
93
- ::Sinatra::Application.get("#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/docs/#{@collection.name}/#{@name}") do
92
+ Rabbit::routes << [:get, "#{settings.root_url}/docs/#{@collection.name}/#{@name}"]
93
+ ::Sinatra::Application.get("#{settings.root_url}/docs/#{@collection.name}/#{@name}") do
94
94
  @collection, @operation = coll, oper
95
95
  @features = driver.features_for_operation(coll.name, oper.name)
96
96
  respond_to do |format|
@@ -102,8 +102,8 @@ module Sinatra
102
102
 
103
103
  def generate_options
104
104
  current_operation = self
105
- Rabbit::routes << [:options, "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{current_operation.collection.name}/#{current_operation.name}"]
106
- ::Sinatra::Application.options("#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{current_operation.collection.name}/#{current_operation.name}") do
105
+ Rabbit::routes << [:options, "#{settings.root_url}/#{current_operation.collection.name}/#{current_operation.name}"]
106
+ ::Sinatra::Application.options("#{settings.root_url}/#{current_operation.collection.name}/#{current_operation.name}") do
107
107
  required_params = current_operation.effective_params(driver).collect do |name, validation|
108
108
  name.to_s if validation.type.eql?(:required)
109
109
  end.compact.join(',')
@@ -121,7 +121,7 @@ module Sinatra
121
121
  @control = Proc.new do
122
122
  op.collection.check_supported(driver)
123
123
  op.check_capability(driver)
124
- op.validate(op.effective_params(driver), params)
124
+ op.validate(driver, op.effective_params(driver), params, credentials)
125
125
  instance_eval(&block)
126
126
  end
127
127
  end
@@ -152,8 +152,8 @@ module Sinatra
152
152
  end
153
153
 
154
154
  def generate
155
- Rabbit::routes << [@method, "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{path}"]
156
- ::Sinatra::Application.send(@method, "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{path}", {}, &@control)
155
+ Rabbit::routes << [@method, "#{settings.root_url}/#{path}"]
156
+ ::Sinatra::Application.send(@method, "#{settings.root_url}/#{path}", {}, &@control)
157
157
  # Set up some Rails-like URL helpers
158
158
  if name == :index
159
159
  gen_route "#{@collection.name}_url"
@@ -202,11 +202,11 @@ module Sinatra
202
202
  class Collection
203
203
  attr_reader :name, :operations, :subcollections
204
204
 
205
- def initialize(name, &block)
205
+ def initialize(name, options={}, &block)
206
206
  @name = name
207
207
  @description = ""
208
208
  @operations, @subcollections = {}, {}
209
- @global = false
209
+ @global = options[:global] || false
210
210
  instance_eval(&block) if block_given?
211
211
  generate_documentation
212
212
  generate_head
@@ -239,8 +239,8 @@ module Sinatra
239
239
 
240
240
  def generate_head
241
241
  current_collection = self
242
- Rabbit::routes << [:head, "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{name}"]
243
- ::Sinatra::Application.head("#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{name}") do
242
+ Rabbit::routes << [:head, "#{settings.root_url}/#{name}"]
243
+ ::Sinatra::Application.head("#{settings.root_url}/#{name}") do
244
244
  methods_allowed = current_collection.operations.collect { |o| o[1].method.to_s.upcase }.uniq.join(',')
245
245
  headers 'Allow' => "HEAD,OPTIONS,#{methods_allowed}"
246
246
  [200, '']
@@ -249,8 +249,8 @@ module Sinatra
249
249
 
250
250
  def generate_options
251
251
  current_collection = self
252
- Rabbit::routes << [:options, "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{name}"]
253
- ::Sinatra::Application.options("#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/#{name}") do
252
+ Rabbit::routes << [:options, "#{settings.root_url}/#{name}"]
253
+ ::Sinatra::Application.options("#{settings.root_url}/#{name}") do
254
254
  operations_allowed = current_collection.operations.collect { |o| o[0] }.join(',')
255
255
  headers 'X-Operations-Allowed' => operations_allowed
256
256
  [200, '']
@@ -259,8 +259,8 @@ module Sinatra
259
259
 
260
260
  def generate_documentation
261
261
  coll = self
262
- Rabbit::routes << [:get, "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/docs/#{@name}"]
263
- ::Sinatra::Application.get("#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/docs/#{@name}") do
262
+ Rabbit::routes << [:get, "#{settings.root_url}/docs/#{@name}"]
263
+ ::Sinatra::Application.get("#{settings.root_url}/docs/#{@name}") do
264
264
  coll.check_supported(driver)
265
265
  @collection = coll
266
266
  @operations = coll.operations
@@ -393,14 +393,19 @@ module Sinatra
393
393
  collections[name].generate
394
394
  end
395
395
 
396
- # Generate a root route for API docs
397
- get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/docs\/?" do
398
- respond_to do |format|
399
- format.html { haml :'docs/index' }
400
- format.xml { haml :'docs/index' }
401
- end
396
+ def global_collection(name, &block)
397
+ raise DuplicateCollectionException if collections[name]
398
+ collections[name] = Collection.new(name, { :global => true }, &block)
399
+ collections[name].generate
402
400
  end
403
401
 
402
+ # Make sure this collection can be accessed, regardless of whether the
403
+ # driver supports it or not
404
+ def global_collection(name, &block)
405
+ raise DuplicateCollectionException if collections[name]
406
+ collections[name] = Collection.new(name, :global => true, &block)
407
+ collections[name].generate
408
+ end
404
409
  end
405
410
 
406
411
  module RabbitHelper
@@ -423,11 +428,14 @@ module Sinatra
423
428
  helpers RabbitHelper
424
429
  end
425
430
 
426
- configure do
427
- class << Sinatra::Base
428
- def options(path, opts={}, &block)
429
- route 'OPTIONS', path, opts, &block
431
+ # In Sinatra < 1.2 there was no helper to create OPTIONS route
432
+ unless Sinatra::Base.respond_to? :options
433
+ configure do
434
+ class << Sinatra::Base
435
+ def options(path, opts={}, &block)
436
+ route 'OPTIONS', path, opts, &block
437
+ end
430
438
  end
439
+ Sinatra::Delegator.delegate :options
431
440
  end
432
- Sinatra::Delegator.delegate :options
433
441
  end