bbrowning-deltacloud-core 0.0.4-java

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 (134) hide show
  1. data/COPYING +176 -0
  2. data/Rakefile +99 -0
  3. data/bin/deltacloudd +120 -0
  4. data/config.ru +5 -0
  5. data/deltacloud.rb +18 -0
  6. data/lib/deltacloud/base_driver/base_driver.rb +229 -0
  7. data/lib/deltacloud/base_driver/features.rb +166 -0
  8. data/lib/deltacloud/base_driver/mock_driver.rb +40 -0
  9. data/lib/deltacloud/base_driver.rb +20 -0
  10. data/lib/deltacloud/drivers/ec2/ec2_driver.rb +410 -0
  11. data/lib/deltacloud/drivers/ec2/ec2_mock_driver.rb +170 -0
  12. data/lib/deltacloud/drivers/gogrid/gogrid_client.rb +50 -0
  13. data/lib/deltacloud/drivers/gogrid/gogrid_driver.rb +332 -0
  14. data/lib/deltacloud/drivers/gogrid/test.rb +13 -0
  15. data/lib/deltacloud/drivers/mock/data/images/img1.yml +3 -0
  16. data/lib/deltacloud/drivers/mock/data/images/img2.yml +3 -0
  17. data/lib/deltacloud/drivers/mock/data/images/img3.yml +3 -0
  18. data/lib/deltacloud/drivers/mock/data/instances/inst0.yml +16 -0
  19. data/lib/deltacloud/drivers/mock/data/instances/inst1.yml +9 -0
  20. data/lib/deltacloud/drivers/mock/data/instances/inst2.yml +9 -0
  21. data/lib/deltacloud/drivers/mock/data/storage_snapshots/snap1.yml +4 -0
  22. data/lib/deltacloud/drivers/mock/data/storage_snapshots/snap2.yml +4 -0
  23. data/lib/deltacloud/drivers/mock/data/storage_snapshots/snap3.yml +4 -0
  24. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol1.yml +6 -0
  25. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol2.yml +6 -0
  26. data/lib/deltacloud/drivers/mock/data/storage_volumes/vol3.yml +6 -0
  27. data/lib/deltacloud/drivers/mock/mock_driver.rb +277 -0
  28. data/lib/deltacloud/drivers/opennebula/cloud_client.rb +116 -0
  29. data/lib/deltacloud/drivers/opennebula/occi_client.rb +204 -0
  30. data/lib/deltacloud/drivers/opennebula/opennebula_driver.rb +241 -0
  31. data/lib/deltacloud/drivers/rackspace/rackspace_client.rb +130 -0
  32. data/lib/deltacloud/drivers/rackspace/rackspace_driver.rb +182 -0
  33. data/lib/deltacloud/drivers/rhevm/rhevm_driver.rb +255 -0
  34. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_client.rb +85 -0
  35. data/lib/deltacloud/drivers/rimuhosting/rimuhosting_driver.rb +166 -0
  36. data/lib/deltacloud/drivers/terremark/terremark_driver.rb +286 -0
  37. data/lib/deltacloud/hardware_profile.rb +153 -0
  38. data/lib/deltacloud/helpers/application_helper.rb +115 -0
  39. data/lib/deltacloud/helpers/conversion_helper.rb +39 -0
  40. data/lib/deltacloud/helpers/hardware_profiles_helper.rb +35 -0
  41. data/lib/deltacloud/helpers.rb +5 -0
  42. data/lib/deltacloud/method_serializer.rb +85 -0
  43. data/lib/deltacloud/models/base_model.rb +59 -0
  44. data/lib/deltacloud/models/image.rb +27 -0
  45. data/lib/deltacloud/models/instance.rb +38 -0
  46. data/lib/deltacloud/models/instance_profile.rb +48 -0
  47. data/lib/deltacloud/models/key.rb +35 -0
  48. data/lib/deltacloud/models/realm.rb +26 -0
  49. data/lib/deltacloud/models/storage_snapshot.rb +27 -0
  50. data/lib/deltacloud/models/storage_volume.rb +28 -0
  51. data/lib/deltacloud/state_machine.rb +84 -0
  52. data/lib/deltacloud/validation.rb +70 -0
  53. data/lib/drivers.rb +50 -0
  54. data/lib/sinatra/accept_media_types.rb +128 -0
  55. data/lib/sinatra/lazy_auth.rb +56 -0
  56. data/lib/sinatra/rabbit.rb +273 -0
  57. data/lib/sinatra/respond_to.rb +272 -0
  58. data/lib/sinatra/static_assets.rb +83 -0
  59. data/lib/sinatra/url_for.rb +53 -0
  60. data/public/favicon.ico +0 -0
  61. data/public/images/grid.png +0 -0
  62. data/public/images/logo-wide.png +0 -0
  63. data/public/images/rails.png +0 -0
  64. data/public/images/topbar-bg.png +0 -0
  65. data/public/javascripts/application.js +32 -0
  66. data/public/javascripts/jquery-1.4.2.min.js +154 -0
  67. data/public/stylesheets/compiled/application.css +613 -0
  68. data/public/stylesheets/compiled/ie.css +31 -0
  69. data/public/stylesheets/compiled/print.css +27 -0
  70. data/public/stylesheets/compiled/screen.css +456 -0
  71. data/server.rb +354 -0
  72. data/support/fedora/deltacloudd +68 -0
  73. data/support/fedora/rubygem-deltacloud-core.spec +91 -0
  74. data/tests/api_test.rb +37 -0
  75. data/tests/hardware_profiles_test.rb +120 -0
  76. data/tests/images_test.rb +111 -0
  77. data/tests/instance_states_test.rb +52 -0
  78. data/tests/instances_test.rb +219 -0
  79. data/tests/realms_test.rb +78 -0
  80. data/tests/url_for_test.rb +50 -0
  81. data/views/accounts/index.html.haml +11 -0
  82. data/views/accounts/show.html.haml +30 -0
  83. data/views/api/show.html.haml +15 -0
  84. data/views/api/show.xml.haml +5 -0
  85. data/views/docs/collection.html.haml +37 -0
  86. data/views/docs/collection.xml.haml +14 -0
  87. data/views/docs/index.html.haml +15 -0
  88. data/views/docs/index.xml.haml +5 -0
  89. data/views/docs/operation.html.haml +31 -0
  90. data/views/docs/operation.xml.haml +10 -0
  91. data/views/errors/auth_exception.html.haml +8 -0
  92. data/views/errors/auth_exception.xml.haml +2 -0
  93. data/views/errors/backend_error.html.haml +19 -0
  94. data/views/errors/backend_error.xml.haml +8 -0
  95. data/views/errors/not_found.html.haml +6 -0
  96. data/views/errors/not_found.xml.haml +2 -0
  97. data/views/errors/validation_failure.html.haml +11 -0
  98. data/views/errors/validation_failure.xml.haml +7 -0
  99. data/views/hardware_profiles/index.html.haml +25 -0
  100. data/views/hardware_profiles/index.xml.haml +4 -0
  101. data/views/hardware_profiles/show.html.haml +19 -0
  102. data/views/hardware_profiles/show.xml.haml +18 -0
  103. data/views/images/index.html.haml +30 -0
  104. data/views/images/index.xml.haml +8 -0
  105. data/views/images/show.html.haml +21 -0
  106. data/views/images/show.xml.haml +5 -0
  107. data/views/instance_states/show.gv.erb +45 -0
  108. data/views/instance_states/show.html.haml +31 -0
  109. data/views/instance_states/show.xml.haml +8 -0
  110. data/views/instances/index.html.haml +30 -0
  111. data/views/instances/index.xml.haml +21 -0
  112. data/views/instances/new.html.haml +55 -0
  113. data/views/instances/show.html.haml +43 -0
  114. data/views/instances/show.xml.haml +49 -0
  115. data/views/keys/index.html.haml +26 -0
  116. data/views/keys/index.xml.haml +4 -0
  117. data/views/keys/new.html.haml +8 -0
  118. data/views/keys/show.html.haml +22 -0
  119. data/views/keys/show.xml.haml +20 -0
  120. data/views/layout.html.haml +26 -0
  121. data/views/realms/index.html.haml +29 -0
  122. data/views/realms/index.xml.haml +10 -0
  123. data/views/realms/show.html.haml +15 -0
  124. data/views/realms/show.xml.haml +9 -0
  125. data/views/root/index.html.haml +4 -0
  126. data/views/storage_snapshots/index.html.haml +20 -0
  127. data/views/storage_snapshots/index.xml.haml +9 -0
  128. data/views/storage_snapshots/show.html.haml +14 -0
  129. data/views/storage_snapshots/show.xml.haml +7 -0
  130. data/views/storage_volumes/index.html.haml +21 -0
  131. data/views/storage_volumes/index.xml.haml +13 -0
  132. data/views/storage_volumes/show.html.haml +20 -0
  133. data/views/storage_volumes/show.xml.haml +11 -0
  134. metadata +334 -0
@@ -0,0 +1,128 @@
1
+ module Rack
2
+ class Request
3
+ # The media types of the HTTP_ACCEPT header ordered according to their
4
+ # "quality" (preference level), without any media type parameters.
5
+ #
6
+ # ===== Examples
7
+ #
8
+ # env['HTTP_ACCEPT'] #=> 'application/xml;q=0.8,text/html,text/plain;q=0.9'
9
+ #
10
+ # req = Rack::Request.new(env)
11
+ # req.accept_media_types #=> ['text/html', 'text/plain', 'application/xml']
12
+ # req.accept_media_types.prefered #=> 'text/html'
13
+ #
14
+ # For more information, see:
15
+ # * Acept header: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
16
+ # * Quality values: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.9
17
+ #
18
+ # ===== Returns
19
+ # AcceptMediaTypes:: ordered list of accept header's media types
20
+ #
21
+ def accept_media_types
22
+ @accept_media_types ||= Rack::AcceptMediaTypes.new(@env['HTTP_ACCEPT'])
23
+ end
24
+ end
25
+
26
+ # AcceptMediaTypes is intended for wrapping env['HTTP_ACCEPT'].
27
+ #
28
+ # It allows ordering of its values (accepted media types) according to their
29
+ # "quality" (preference level).
30
+ #
31
+ # This wrapper is typically used to determine the request's prefered media
32
+ # type (see example below).
33
+ #
34
+ # ===== Examples
35
+ #
36
+ # env['HTTP_ACCEPT'] #=> 'application/xml;q=0.8,text/html,text/plain;q=0.9'
37
+ #
38
+ # types = Rack::AcceptMediaTypes.new(env['HTTP_ACCEPT'])
39
+ # types #=> ['text/html', 'text/plain', 'application/xml']
40
+ # types.prefered #=> 'text/html'
41
+ #
42
+ # ===== Notes
43
+ #
44
+ # For simplicity, media type parameters are striped, as they are seldom used
45
+ # in practice. Users who need them are excepted to parse the Accept header
46
+ # manually.
47
+ #
48
+ # ===== References
49
+ #
50
+ # HTTP 1.1 Specs:
51
+ # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
52
+ # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.9
53
+ #
54
+ class AcceptMediaTypes < Array
55
+
56
+ #--
57
+ # NOTE
58
+ # Reason for special handling of nil accept header:
59
+ #
60
+ # "If no Accept header field is present, then it is assumed that the client
61
+ # accepts all media types."
62
+ #
63
+ def initialize(header)
64
+ if header.nil?
65
+ replace(['*/*'])
66
+ else
67
+ replace(order(header.gsub(/ /, '').split(/,/)))
68
+ end
69
+ end
70
+
71
+ # The client's prefered media type.
72
+ def prefered
73
+ first
74
+ end
75
+
76
+ private
77
+
78
+ # Order media types by quality values, remove invalid types, and return media ranges.
79
+ #
80
+ def order(types) #:nodoc:
81
+ types.map {|type| AcceptMediaType.new(type) }.reverse.sort.reverse.select {|type| type.valid? }.map {|type| type.range }
82
+ end
83
+
84
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
85
+ #
86
+ class AcceptMediaType #:nodoc:
87
+ include Comparable
88
+
89
+ # media-range = ( "*/*"
90
+ # | ( type "/" "*" )
91
+ # | ( type "/" subtype )
92
+ # ) *( ";" parameter )
93
+ attr_accessor :range
94
+
95
+ # qvalue = ( "0" [ "." 0*3DIGIT ] )
96
+ # | ( "1" [ "." 0*3("0") ] )
97
+ attr_accessor :quality
98
+
99
+ def initialize(type)
100
+ self.range, *params = type.split(';')
101
+ self.quality = extract_quality(params)
102
+ end
103
+
104
+ def <=>(type)
105
+ self.quality <=> type.quality
106
+ end
107
+
108
+ # "A weight is normalized to a real number in the range 0 through 1,
109
+ # where 0 is the minimum and 1 the maximum value. If a parameter has a
110
+ # quality value of 0, then content with this parameter is `not
111
+ # acceptable' for the client."
112
+ #
113
+ def valid?
114
+ self.quality.between?(0.1, 1)
115
+ end
116
+
117
+ private
118
+ # Extract value from 'q=FLOAT' parameter if present, otherwise assume 1
119
+ #
120
+ # "The default value is q=1."
121
+ #
122
+ def extract_quality(params)
123
+ q = params.detect {|p| p.match(/q=\d\.?\d{0,3}/) }
124
+ q ? q.split('=').last.to_f : 1.0
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,56 @@
1
+ require 'sinatra/base'
2
+
3
+ # Lazy Basic HTTP authentication. Authentication is only forced when the
4
+ # credentials are actually needed.
5
+ module Sinatra
6
+ module LazyAuth
7
+ class LazyCredentials
8
+ def initialize(app)
9
+ @app = app
10
+ @provided = false
11
+ end
12
+
13
+ def user
14
+ credentials!
15
+ @user
16
+ end
17
+
18
+ def password
19
+ credentials!
20
+ @password
21
+ end
22
+
23
+ def provided?
24
+ @provided
25
+ end
26
+
27
+ private
28
+ def credentials!
29
+ unless provided?
30
+ auth = Rack::Auth::Basic::Request.new(@app.request.env)
31
+ unless auth.provided? && auth.basic? && auth.credentials
32
+ @app.authorize!
33
+ end
34
+ @user = auth.credentials[0]
35
+ @password = auth.credentials[1]
36
+ @provided = true
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ def authorize!
43
+ r = "#{DRIVER}-deltacloud@#{HOSTNAME}"
44
+ response['WWW-Authenticate'] = %(Basic realm="#{r}")
45
+ throw(:halt, [401, "Not authorized\n"])
46
+ end
47
+
48
+ # Request the current user's credentials. Actual credentials are only
49
+ # requested when an attempt is made to get the user name or password
50
+ def credentials
51
+ LazyCredentials.new(self)
52
+ end
53
+ end
54
+
55
+ helpers LazyAuth
56
+ end
@@ -0,0 +1,273 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/url_for'
3
+ require 'deltacloud/validation'
4
+
5
+ module Sinatra
6
+
7
+ module Rabbit
8
+
9
+ class DuplicateParamException < Exception; end
10
+ class DuplicateOperationException < Exception; end
11
+ class DuplicateCollectionException < Exception; end
12
+
13
+ class Operation
14
+ attr_reader :name, :method
15
+
16
+ include ::Deltacloud::Validation
17
+
18
+ STANDARD = {
19
+ :index => { :method => :get, :member => false },
20
+ :show => { :method => :get, :member => true },
21
+ :create => { :method => :post, :member => false },
22
+ :update => { :method => :put, :member => true },
23
+ :destroy => { :method => :delete, :member => true }
24
+ }
25
+
26
+ def initialize(coll, name, opts, &block)
27
+ @name = name.to_sym
28
+ opts = STANDARD[@name].merge(opts) if standard?
29
+ @collection = coll
30
+ raise "No method for operation #{name}" unless opts[:method]
31
+ @method = opts[:method].to_sym
32
+ @member = opts[:member]
33
+ @description = ""
34
+ instance_eval(&block) if block_given?
35
+ generate_documentation
36
+ end
37
+
38
+ def standard?
39
+ STANDARD.keys.include?(name)
40
+ end
41
+
42
+ def description(text="")
43
+ return @description if text.blank?
44
+ @description = text
45
+ end
46
+
47
+ def generate_documentation
48
+ coll, oper = @collection, self
49
+ ::Sinatra::Application.get("/api/docs/#{@collection.name}/#{@name}") do
50
+ @collection, @operation = coll, oper
51
+ respond_to do |format|
52
+ format.html { haml :'docs/operation' }
53
+ format.xml { haml :'docs/operation' }
54
+ end
55
+ end
56
+ end
57
+
58
+ def control(&block)
59
+ op = self
60
+ @control = Proc.new do
61
+ op.validate(params)
62
+ instance_eval(&block)
63
+ end
64
+ end
65
+
66
+ def prefix
67
+ # FIXME: Make the /api prefix configurable
68
+ "/api"
69
+ end
70
+
71
+ def path(args = {})
72
+ l_prefix = args[:prefix] ? args[:prefix] : prefix
73
+ if @member
74
+ if standard?
75
+ "#{l_prefix}/#{@collection.name}/:id"
76
+ else
77
+ "#{l_prefix}/#{@collection.name}/:id/#{name}"
78
+ end
79
+ else
80
+ "#{l_prefix}/#{@collection.name}"
81
+ end
82
+ end
83
+
84
+ def generate
85
+ ::Sinatra::Application.send(@method, path, {}, &@control)
86
+ # Set up some Rails-like URL helpers
87
+ if name == :index
88
+ gen_route "#{@collection.name}_url"
89
+ elsif name == :show
90
+ gen_route "#{@collection.name.to_s.singularize}_url"
91
+ else
92
+ gen_route "#{name}_#{@collection.name.to_s.singularize}_url"
93
+ end
94
+ end
95
+
96
+ private
97
+ def gen_route(name)
98
+ route_url = path
99
+ if @member
100
+ ::Sinatra::Application.send(:define_method, name) do |id, *args|
101
+ url = query_url(route_url, args[0])
102
+ url_for url.gsub(/:id/, id.to_s), :full
103
+ end
104
+ else
105
+ ::Sinatra::Application.send(:define_method, name) do |*args|
106
+ url = query_url(route_url, args[0])
107
+ url_for url, :full
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ class Collection
114
+ attr_reader :name, :operations
115
+
116
+ def initialize(name, &block)
117
+ @name = name
118
+ @description = ""
119
+ @operations = {}
120
+ instance_eval(&block) if block_given?
121
+ generate_documentation
122
+ end
123
+
124
+ # Set/Return description for collection
125
+ # If first parameter is not present, full description will be
126
+ # returned.
127
+ def description(text='')
128
+ return @description if text.blank?
129
+ @description = text
130
+ end
131
+
132
+ def generate_documentation
133
+ coll, oper, features = self, @operations, driver.features(name)
134
+ ::Sinatra::Application.get("/api/docs/#{@name}") do
135
+ @collection, @operations, @features = coll, oper, features
136
+ respond_to do |format|
137
+ format.html { haml :'docs/collection' }
138
+ format.xml { haml :'docs/collection' }
139
+ end
140
+ end
141
+ end
142
+
143
+ # Add a new operation for this collection. For the standard REST
144
+ # operations :index, :show, :update, and :destroy, we already know
145
+ # what method to use and whether this is an operation on the URL for
146
+ # individual elements or for the whole collection.
147
+ #
148
+ # For non-standard operations, options must be passed:
149
+ # :method : one of the HTTP methods
150
+ # :member : whether this is an operation on the collection or an
151
+ # individual element (FIXME: custom operations on the
152
+ # collection will use a nonsensical URL) The URL for the
153
+ # operation is the element URL with the name of the operation
154
+ # appended
155
+ #
156
+ # This also defines a helper method like show_instance_url that returns
157
+ # the URL to this operation (in request context)
158
+ def operation(name, opts = {}, &block)
159
+ raise DuplicateOperationException if @operations[name]
160
+ @operations[name] = Operation.new(self, name, opts, &block)
161
+ end
162
+
163
+ def generate
164
+ operations.values.each { |op| op.generate }
165
+ app = ::Sinatra::Application
166
+ collname = name # Work around Ruby's weird scoping/capture
167
+ app.send(:define_method, "#{name.to_s.singularize}_url") do |id|
168
+ url_for "/api/#{collname}/#{id}", :full
169
+ end
170
+
171
+ if index_op = operations[:index]
172
+ app.send(:define_method, "#{name}_url") do
173
+ url_for index_op.path.gsub(/\/\?$/,''), :full
174
+ end
175
+ end
176
+ end
177
+
178
+ def add_feature_params(features)
179
+ features.each do |f|
180
+ f.operations.each do |fop|
181
+ if cop = operations[fop.name]
182
+ fop.params.each_key do |k|
183
+ if cop.params.has_key?(k)
184
+ raise DuplicateParamException, "Parameter '#{k}' for operation #{fop.name} defined by collection #{@name} and by feature #{f.name}"
185
+ else
186
+ cop.params[k] = fop.params[k]
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ def collections
196
+ @collections ||= {}
197
+ end
198
+
199
+ # Create a new collection. NAME should be the pluralized name of the
200
+ # collection.
201
+ #
202
+ # Adds a helper method #{name}_url which returns the URL to the :index
203
+ # operation on this collection.
204
+ def collection(name, &block)
205
+ raise DuplicateCollectionException if collections[name]
206
+ return unless driver.has_collection?(name.to_sym)
207
+ collections[name] = Collection.new(name, &block)
208
+ collections[name].add_feature_params(driver.features(name))
209
+ collections[name].generate
210
+ end
211
+
212
+ # Generate a root route for API docs
213
+ get '/api/docs\/?' do
214
+ respond_to do |format|
215
+ format.html { haml :'docs/index' }
216
+ format.xml { haml :'docs/index' }
217
+ end
218
+ end
219
+
220
+ end
221
+
222
+ module RabbitHelper
223
+ def query_url(url, params)
224
+ return url if params.nil? || params.empty?
225
+ url + "?#{URI.escape(params.collect{|k,v| "#{k}=#{v}"}.join('&'))}"
226
+ end
227
+
228
+ def entry_points
229
+ collections.values.inject([]) do |m, coll|
230
+ url = url_for coll.operations[:index].path, :full
231
+ m << [ coll.name, url ]
232
+ end
233
+ end
234
+ end
235
+
236
+ register Rabbit
237
+ helpers RabbitHelper
238
+ end
239
+
240
+ class String
241
+ # Rails defines this for a number of other classes, including Object
242
+ # see activesupport/lib/active_support/core_ext/object/blank.rb
243
+ def blank?
244
+ self !~ /\S/
245
+ end
246
+
247
+ # Title case.
248
+ #
249
+ # "this is a string".titlecase
250
+ # => "This Is A String"
251
+ #
252
+ # CREDIT: Eliazar Parra
253
+ # Copied from facets
254
+ def titlecase
255
+ gsub(/\b\w/){ $`[-1,1] == "'" ? $& : $&.upcase }
256
+ end
257
+
258
+ def pluralize
259
+ self + "s"
260
+ end
261
+
262
+ def singularize
263
+ self.gsub(/s$/, '')
264
+ end
265
+
266
+ def underscore
267
+ gsub(/::/, '/').
268
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
269
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
270
+ tr("-", "_").
271
+ downcase
272
+ end
273
+ end