ruby-atmos-pure 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +8 -0
- data/README +129 -0
- data/Rakefile +103 -0
- data/lib/atmos.rb +35 -0
- data/lib/atmos/attributes.rb +545 -0
- data/lib/atmos/exceptions.rb +28 -0
- data/lib/atmos/object.rb +321 -0
- data/lib/atmos/parser.rb +110 -0
- data/lib/atmos/parser/rexml.rb +1 -0
- data/lib/atmos/request.rb +221 -0
- data/lib/atmos/response.rb +116 -0
- data/lib/atmos/rest.rb +145 -0
- data/lib/atmos/store.rb +220 -0
- data/lib/atmos/util.rb +40 -0
- data/lib/atmos/version.rb +12 -0
- data/test/credentials.rb +141 -0
- data/test/esutest.rb +499 -0
- data/test/files/SmallImageForTest.iso +0 -0
- data/test/files/dragaf-tiny-from-vsphere.ova +0 -0
- data/test/files/something.txt +1 -0
- data/test/require_test.rb +2 -0
- data/test/suite.rb +11 -0
- data/test/suite_noproxy.rb +12 -0
- data/test/suite_proxy.rb +14 -0
- data/test/test_acl.rb +283 -0
- data/test/test_metadata.rb +162 -0
- data/test/test_object_create.rb +167 -0
- data/test/test_object_get.rb +55 -0
- data/test/test_object_misc.rb +80 -0
- data/test/test_object_read.rb +98 -0
- data/test/test_object_update.rb +59 -0
- data/test/test_request.rb +49 -0
- data/test/test_store.rb +125 -0
- data/test/test_util.rb +58 -0
- metadata +155 -0
@@ -0,0 +1 @@
|
|
1
|
+
# This does nothing except signal that the chosen XML parser to use is REXML
|
@@ -0,0 +1,221 @@
|
|
1
|
+
module Atmos
|
2
|
+
class Request # :nodoc:
|
3
|
+
|
4
|
+
def initialize(options = {})
|
5
|
+
valid = [:store, :default_tag].freeze
|
6
|
+
invalid = options.keys - valid
|
7
|
+
raise Atmos::Exceptions::ArgumentException,
|
8
|
+
"Unrecognized options: #{invalid.inspect}" if (!invalid.empty?)
|
9
|
+
Atmos::LOG.debug("Request.initialize: options: #{options.inspect}")
|
10
|
+
|
11
|
+
@baseurl = options[:store].uri
|
12
|
+
@uid = options[:store].uid
|
13
|
+
@secret = options[:store].secret
|
14
|
+
@http = options[:store].http
|
15
|
+
@tag = options[:default_tag]
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
#
|
20
|
+
# If there is no namespace option provided, we're using the object interface,
|
21
|
+
# otherwise we're using the namespace interface. Determine which it is, so we know
|
22
|
+
# which URI to use.
|
23
|
+
#
|
24
|
+
# If aoid is nil, we're creating a new object regardless.
|
25
|
+
#
|
26
|
+
def get_uri(action, options)
|
27
|
+
|
28
|
+
if (options.has_key?(:namespace) && action.has_key?(:namespace_uri))
|
29
|
+
|
30
|
+
# only two actions need namespace, so if the action doesn't have the
|
31
|
+
# :namespace_url key, we use the regular :object_url.
|
32
|
+
ns = (options[:namespace].start_with?('/')) ? options[:namespace][1..-1] : options[:namespace]
|
33
|
+
uri = action[:namespace_uri].sub(/:namespace/, ns)
|
34
|
+
|
35
|
+
else
|
36
|
+
|
37
|
+
uri = action[:object_uri]
|
38
|
+
|
39
|
+
# only need the following check/substution if the uri requires an id
|
40
|
+
if (!uri.index(':id').nil?)
|
41
|
+
|
42
|
+
if (options.has_key?(:id) && !options[:id].nil?)
|
43
|
+
uri = uri.sub(/:id/, options[:id])
|
44
|
+
else
|
45
|
+
raise Atmos::Exceptions::ArgumentException, "An id is required for this action. This is an internal error."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Atmos::LOG.info("uri: #{uri}")
|
51
|
+
uri
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
def do(actionname, options = {}, &block)
|
57
|
+
verbs = [:head, :get, :put, :post, :delete].freeze
|
58
|
+
Atmos::LOG.info("do #{actionname} options: #{options.inspect}")
|
59
|
+
raise Atmos::Exceptions::InternalLibraryException,
|
60
|
+
"Invalid REST action: #{actionname}" if (REST[actionname].nil?)
|
61
|
+
|
62
|
+
action = REST[actionname]
|
63
|
+
uri = get_uri(action, options)
|
64
|
+
url = URI::join(@baseurl.to_s, uri.to_s)
|
65
|
+
verb = action[:verb]
|
66
|
+
|
67
|
+
request = (verbs.include?(verb)) ? Net::HTTP.const_get(verb.to_s.capitalize).new(uri) : nil
|
68
|
+
raise Atmos::Exceptions::AtmosException,
|
69
|
+
"Couldn't create Net::HTTP request object for #{verb}" if (request.nil?)
|
70
|
+
|
71
|
+
headers = {}
|
72
|
+
headers['Date'] = Time.now().httpdate()
|
73
|
+
headers['x-emc-date'] = Time.now().httpdate()
|
74
|
+
headers['x-emc-uid'] = @uid
|
75
|
+
|
76
|
+
if (!action[:required_headers].nil?)
|
77
|
+
action[:required_headers].each do |header|
|
78
|
+
case header
|
79
|
+
when 'Content-Type' then
|
80
|
+
headers[header] = (!options[:mimetype].nil?) ? options[:mimetype] : 'binary/octet-stream'
|
81
|
+
when 'Content-Length' then
|
82
|
+
headers[header] = (!options[:length].nil?) ? options[:length] : 0
|
83
|
+
when 'x-emc-tags' then
|
84
|
+
headers[header] = (!options[header].nil?) ? options[header] : ""
|
85
|
+
else
|
86
|
+
if (options[header])
|
87
|
+
headers[header] = options[header]
|
88
|
+
else
|
89
|
+
raise "Value not supplied for required header: '#{header}'"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
if (!action[:optional_headers].nil?)
|
96
|
+
action[:optional_headers].each do |header|
|
97
|
+
if (header.eql?('Range') && options.has_key?('Range') && !options['Range'].nil?)
|
98
|
+
r = options['Range']
|
99
|
+
options.delete('Range')
|
100
|
+
#headers[header] = (r.kind_of?(Range)) ? "Bytes=#{r.first}-#{r.last}" : "Bytes=#{r.to_s}"
|
101
|
+
if (r.kind_of?(Range))
|
102
|
+
headers[header] = "Bytes=#{r.first}-#{r.last}"
|
103
|
+
Atmos::LOG.info("Request.initialize: given Range object: #{headers[header]}")
|
104
|
+
else
|
105
|
+
Atmos::LOG.info("Request.initialize: given range string: #{headers[header]}")
|
106
|
+
headers[header] = "Bytes=#{r.to_s}"
|
107
|
+
end
|
108
|
+
if ((!options[:data].nil?) and ((r.end-r.begin+1) != options[:data].length))
|
109
|
+
raise Atmos::Exceptions::ArgumentException, "The range length (#{r.end - r.begin + 1}) doesn't match the data length (#{options[:data].length})."
|
110
|
+
end
|
111
|
+
end
|
112
|
+
key = (action[:header2sym] && action[:header2sym][header]) ? action[:header2sym][header] : header
|
113
|
+
|
114
|
+
if (!options[key].nil?)
|
115
|
+
if (options[key].kind_of?(Hash))
|
116
|
+
headers[header] = ""
|
117
|
+
options[key].each do |key,val|
|
118
|
+
headers[header] += "#{key}=#{val}, "
|
119
|
+
end
|
120
|
+
headers[header].chop!
|
121
|
+
headers[header].chop!
|
122
|
+
elsif (options[key].kind_of?(Array))
|
123
|
+
headers[header] = ""
|
124
|
+
options[key].each do |val|
|
125
|
+
headers[header] += "#{val}, "
|
126
|
+
end
|
127
|
+
headers[header].chop!
|
128
|
+
headers[header].chop!
|
129
|
+
elsif (options[key].respond_to?(:to_s))
|
130
|
+
headers[header] = options[key]
|
131
|
+
else
|
132
|
+
raise "value for optional header '#{header}' id bad type: #{options[header].class}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
if (!@tag.nil? && !action[:optional_headers].nil? && action[:optional_headers].include?('x-emc-listable-meta'))
|
139
|
+
if (headers.has_key?('x-emc-listable-meta'))
|
140
|
+
headers['x-emc-listable-meta'] += ", #{@tag}=atmos-ruby-default"
|
141
|
+
else
|
142
|
+
headers['x-emc-listable-meta'] = "#{@tag}=atmos-ruby-default"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
headers['x-emc-signature'] = calculate_signature(action[:verb], @secret, url, headers)
|
147
|
+
|
148
|
+
if (options[:data])
|
149
|
+
Atmos::LOG.info("there is data: [#{options[:data]}]")
|
150
|
+
if (options[:data].respond_to?(:read))
|
151
|
+
request.body_stream = options[:data]
|
152
|
+
else
|
153
|
+
request.body = options[:data]
|
154
|
+
end
|
155
|
+
headers['Content-Length'] =
|
156
|
+
options[:data].respond_to?(:lstat) ? options[:data].stat.size : options[:data].size
|
157
|
+
Atmos::LOG.info("set Content-Length to: #{headers['Content-Length']}\n")
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
headers.each do |key,val|
|
162
|
+
request[key] = val
|
163
|
+
end
|
164
|
+
|
165
|
+
@http.set_debug_output($stdout) if (Atmos::LOG.level == Log4r::DEBUG)
|
166
|
+
|
167
|
+
|
168
|
+
#
|
169
|
+
# so this is weird. because some of these atmos request may be
|
170
|
+
# insanely long, we need to allow a mechanism for our API layer
|
171
|
+
# to read progressively, not only all at once
|
172
|
+
#
|
173
|
+
if (block)
|
174
|
+
@http.request(request) do |response|
|
175
|
+
block.call(response)
|
176
|
+
end
|
177
|
+
return
|
178
|
+
end
|
179
|
+
|
180
|
+
response = @http.request(request)
|
181
|
+
Atmos::Parser::response_check_action_error(actionname, response)
|
182
|
+
|
183
|
+
Atmos::Response.new(response, actionname)
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
def calculate_signature(verb, secret, url, headers)
|
188
|
+
headers_copy = headers.clone
|
189
|
+
|
190
|
+
# normalize emc headers
|
191
|
+
headers_copy.each do |header, value|
|
192
|
+
if (header.start_with?('x-emc-'))
|
193
|
+
headers.delete(header)
|
194
|
+
headers[header.to_s.strip.downcase] = value.to_s.strip.sub(/\s+/, ' ').sub(/\n/, '')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# string together all emc headers, no newline at end
|
199
|
+
emc = ""
|
200
|
+
headers.keys.sort.each do |header|
|
201
|
+
if (header.start_with?('x-emc-'))
|
202
|
+
emc += "#{header}:#{headers[header]}\n"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
emc.chomp!
|
206
|
+
|
207
|
+
hashstring = verb.to_s.upcase+"\n"
|
208
|
+
['Content-Type', 'Range', 'Date'].each do |key|
|
209
|
+
hashstring += headers[key] if (!headers[key].nil?)
|
210
|
+
hashstring += "\n"
|
211
|
+
end
|
212
|
+
|
213
|
+
hashstring += url.path
|
214
|
+
hashstring += '?'+url.query if (!url.query.nil?)
|
215
|
+
hashstring += "\n"+ emc
|
216
|
+
|
217
|
+
Atmos::LOG.debug("calculate_signature: hashstring: #{hashstring}")
|
218
|
+
Base64.encode64(HMAC::SHA1.digest(Base64.decode64(secret), hashstring)).chomp()
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Atmos
|
2
|
+
class Response # :nodoc:
|
3
|
+
attr_reader :http_response
|
4
|
+
|
5
|
+
def initialize(response, action)
|
6
|
+
@http_response = response
|
7
|
+
@action = action
|
8
|
+
|
9
|
+
if (response.kind_of?(Net::HTTPServerError))
|
10
|
+
raise Atmos::Exceptions::ServerException, response.message
|
11
|
+
elsif (response.kind_of?(Net::HTTPClientError))
|
12
|
+
|
13
|
+
raise Atmos::Exceptions::AtmosException, "Atmos got a bad request. This is probably a problem in this library." if (response.kind_of?(Net::HTTPBadRequest))
|
14
|
+
|
15
|
+
Atmos::LOG.debug("#{response.class}")
|
16
|
+
Atmos::LOG.debug("#{response.body}")
|
17
|
+
|
18
|
+
Atmos::Parser::response_check_action_error(@action, response)
|
19
|
+
|
20
|
+
elsif (!response.kind_of?(Net::HTTPSuccess))
|
21
|
+
raise Atmos::Exceptions::NotImplementedException, "This library doesn't handle these kinds of responses: #{response}"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def each(header)
|
27
|
+
if (!self[header].nil?)
|
28
|
+
self[header].split(',').each do |elt|
|
29
|
+
if (!elt.index('=').nil?)
|
30
|
+
key,val = elt.split('=')
|
31
|
+
yield key.strip, val.strip
|
32
|
+
else
|
33
|
+
yield elt.strip
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def body
|
40
|
+
@http_response.body
|
41
|
+
end
|
42
|
+
|
43
|
+
def method_missing(sym, *args)
|
44
|
+
Atmos::LOG.info("method missing: #{sym}")
|
45
|
+
header = "x-emc-#{sym.id2name.sub(/_/, '-')}"
|
46
|
+
if (REST[@action][:return_headers].include?(header))
|
47
|
+
Atmos::LOG.info("header: #{header}")
|
48
|
+
rv = Atmos::Util.header2hash(header, @http_response[header])
|
49
|
+
Atmos::LOG.debug("rv: #{rv.inspect}")
|
50
|
+
return rv
|
51
|
+
else
|
52
|
+
return super.method_missing(sym, *args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def delta
|
57
|
+
if (REST[@action][:return_headers].include?('x-emc-delta'))
|
58
|
+
@http_response['x-emc-delta']
|
59
|
+
else
|
60
|
+
raise "A #{@action} request doesn't return a delta."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def policy
|
65
|
+
if (REST[@action][:return_headers].include?('x-emc-policy'))
|
66
|
+
@http_response['x-emc-policy']
|
67
|
+
else
|
68
|
+
raise "A #{@action} request doesn't return a policy description."
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def id
|
73
|
+
if (REST[@action][:return_headers].include?('location'))
|
74
|
+
@http_response['location'][@http_response['location'].rindex('/')+1..-1]
|
75
|
+
else
|
76
|
+
raise "A #{@action} request doesn't return an id."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def headers
|
81
|
+
headers = {}
|
82
|
+
@http_response.each do |header, value|
|
83
|
+
headers[header] = value
|
84
|
+
end
|
85
|
+
headers
|
86
|
+
end
|
87
|
+
|
88
|
+
def [](header)
|
89
|
+
@http_response[header]
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
# while (response.kind_of?(Net::HTTPRedirection))
|
100
|
+
|
101
|
+
#From http://railstips.org/blog/archives/2009/03/04/following-redirects-with-nethttp/
|
102
|
+
# rurl = (response['location'].nil?) ? response.body.match(/<a href=\"([^>]+)\">/i)[1] : response['location']
|
103
|
+
|
104
|
+
# puts("Got a redirect \nfrom: #{options[:url]}\n to: #{rurl}")
|
105
|
+
|
106
|
+
# if rurl.start_with?('/')
|
107
|
+
# puts("Got a relative redirect url: #{options[:url]}")
|
108
|
+
# options[:url] = URI.parse("#{url.scheme}://#{url.host}#{redirect_url}")
|
109
|
+
# end
|
110
|
+
|
111
|
+
# options[:redirects] = (options[:redirects].nil?) ? 0 : options[:redirects] += 1
|
112
|
+
# response = self.generic_request(options)
|
113
|
+
# end
|
114
|
+
# raise "Too many redirects (#{options[:redirects]}): #{url}" if (!options[:redirects].nil? && (options[:redirects] > 3))
|
115
|
+
|
116
|
+
end
|
data/lib/atmos/rest.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
module Atmos
|
2
|
+
REST = {
|
3
|
+
:listable_tags => { :verb => :get,
|
4
|
+
:object_uri => '/rest/objects?listabletags',
|
5
|
+
:required_headers => ['x-emc-tags'],
|
6
|
+
:optional_headers => ['x-emc-token'],
|
7
|
+
:http_response => 200,
|
8
|
+
:return_headers => ['x-emc-listable-tags'],
|
9
|
+
},
|
10
|
+
:set_group_acl => { :verb => :post,
|
11
|
+
:object_uri => '/rest/objects/:id?acl',
|
12
|
+
:required_headers => ['x-emc-groupacl'],
|
13
|
+
:http_response => 200,
|
14
|
+
},
|
15
|
+
:set_user_acl => { :verb => :post,
|
16
|
+
:object_uri => '/rest/objects/:id?acl',
|
17
|
+
:required_headers => ['x-emc-useracl'],
|
18
|
+
:http_response => 200,
|
19
|
+
},
|
20
|
+
:set_metadata => { :verb => :post,
|
21
|
+
:object_uri => '/rest/objects/:id?metadata/user',
|
22
|
+
:http_response => 200,
|
23
|
+
:required_headers => ['x-emc-meta'],
|
24
|
+
},
|
25
|
+
:set_listable_metadata => { :verb => :post,
|
26
|
+
:object_uri => '/rest/objects/:id?metadata/user',
|
27
|
+
:http_response => 200,
|
28
|
+
:required_headers => ['x-emc-listable-meta'],
|
29
|
+
},
|
30
|
+
:delete_metadata => { :verb => :delete,
|
31
|
+
:object_uri => '/rest/objects/:id?metadata/user',
|
32
|
+
:http_response => 204,
|
33
|
+
:required_headers => ['x-emc-tags'],
|
34
|
+
},
|
35
|
+
:read_object => { :verb => :get,
|
36
|
+
:object_uri => '/rest/objects/:id',
|
37
|
+
:http_response => 200,
|
38
|
+
:optional_headers => ['Range'],
|
39
|
+
},
|
40
|
+
:update_object => { :verb => :put,
|
41
|
+
:object_uri => '/rest/objects/:id',
|
42
|
+
:http_response => 200,
|
43
|
+
:required_headers => ['Content-Type', 'Content-Length'],
|
44
|
+
:optional_headers => ['x-emc-useracl', 'x-emc-groupacl', 'Range'],
|
45
|
+
},
|
46
|
+
:trunc_object => { :verb => :put,
|
47
|
+
:object_uri => '/rest/objects/:id',
|
48
|
+
:http_response => 200,
|
49
|
+
:required_headers => ['Content-Length'],
|
50
|
+
:optional_headers => ['x-emc-useracl', 'x-emc-groupacl', 'Range'],
|
51
|
+
},
|
52
|
+
:get_service_info => { :verb => :get,
|
53
|
+
:object_uri => '/rest/service',
|
54
|
+
:response => :xml,
|
55
|
+
:http_response => 200,
|
56
|
+
},
|
57
|
+
|
58
|
+
:list_objects => { :verb => :get,
|
59
|
+
:object_uri => '/rest/objects',
|
60
|
+
:response => :xml,
|
61
|
+
:http_response => 200,
|
62
|
+
:required_headers => ['x-emc-tags', 'Content-Type'],
|
63
|
+
:optional_headers => ['x-emc-include-meta', 'x-emc-tags'],
|
64
|
+
:errors => { '1003' => { :class => Atmos::Exceptions::ArgumentException ,
|
65
|
+
:message => "No such listable tag found." } },
|
66
|
+
},
|
67
|
+
|
68
|
+
:create_object => { :verb => :post,
|
69
|
+
:namespace_uri => '/rest/namespace/:namespace',
|
70
|
+
:object_uri => '/rest/objects',
|
71
|
+
:http_response => 201,
|
72
|
+
:optional_headers => ['x-emc-wschecksum', 'x-emc-useracl', 'x-emc-groupacl', 'x-emc-meta', 'x-emc-listable-meta'],
|
73
|
+
:required_headers => ['Content-Type', 'Content-Length'],
|
74
|
+
:return_headers => ['x-emc-delta', 'x-emc-policy', 'location'],
|
75
|
+
:header2sym => {'x-emc-wschecksum' => :checksum,
|
76
|
+
'x-emc-useracl' => :user_acl,
|
77
|
+
'x-emc-groupacl' => :group_acl,
|
78
|
+
'x-emc-meta' => :metadata,
|
79
|
+
'x-emc-listable-meta' => :listable_metadata},
|
80
|
+
},
|
81
|
+
|
82
|
+
:get_object_info => { :verb => :get,
|
83
|
+
:namespace_uri => '/rest/namespace/:namespace?info',
|
84
|
+
:object_uri => '/rest/objects/:id?info',
|
85
|
+
:http_response => 200,
|
86
|
+
:return_headers => ['x-emc-policy'],
|
87
|
+
},
|
88
|
+
:delete_object => { :verb => :delete,
|
89
|
+
:object_uri => '/rest/objects/:id',
|
90
|
+
:http_response => 204,
|
91
|
+
:required_headers => ['Content-Type'],
|
92
|
+
:return_headers => ['x-emc-delta', 'x-emc-policy'],
|
93
|
+
:errors => { '1003' => { :class => Atmos::Exceptions::NoSuchObjectException ,
|
94
|
+
:message => "Object not found to delete." } },
|
95
|
+
},
|
96
|
+
|
97
|
+
:list_system_metadata=> { :verb => :get,
|
98
|
+
:object_uri => '/rest/objects/:id?metadata/system',
|
99
|
+
:http_response => 200,
|
100
|
+
:return_headers => ['x-emc-meta'],
|
101
|
+
:errors => { '1003' => { :class => Atmos::Exceptions::NoSuchObjectException ,
|
102
|
+
:message => "Object not found to delete." } },
|
103
|
+
},
|
104
|
+
:list_metadata => { :verb => :get,
|
105
|
+
:object_uri => '/rest/objects/:id?metadata/user',
|
106
|
+
:http_response => 200,
|
107
|
+
:required_headers => ['Content-Type'],
|
108
|
+
:return_headers => ['x-emc-meta', 'x-emc-listable-meta'],
|
109
|
+
},
|
110
|
+
|
111
|
+
:list_tags => { :verb => :get,
|
112
|
+
:object_uri => '/rest/objects/:id?metadata/tags',
|
113
|
+
:http_response => 200,
|
114
|
+
:return_headers => ['x-emc-tags', 'x-emc-listable-tags'],
|
115
|
+
},
|
116
|
+
|
117
|
+
:list_acl => { :verb => :get,
|
118
|
+
:object_uri => '/rest/objects/:id?acl',
|
119
|
+
:http_response => 200,
|
120
|
+
:return_headers => ['x-emc-useracl', 'x-emc-groupacl'],
|
121
|
+
},
|
122
|
+
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
:delete_version => { :verb => :delete,
|
130
|
+
:object_uri => '/rest/objects/:id?versions',
|
131
|
+
:http_response => 204,
|
132
|
+
},
|
133
|
+
:create_version => { :verb => :post,
|
134
|
+
:object_uri => '/rest/objects/:id?versions' ,
|
135
|
+
:http_response => 201,
|
136
|
+
},
|
137
|
+
:list_versions => { :verb => :get,
|
138
|
+
:object_uri => '/rest/objects/:id?versions',
|
139
|
+
:http_response => 200},
|
140
|
+
:restore_version => { :verb => :put,
|
141
|
+
:object_uri => '/rest/objects/:id?versions',
|
142
|
+
:http_response => 200},
|
143
|
+
} # :nodoc:
|
144
|
+
|
145
|
+
end
|