ruby-atmos 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +8 -0
- data/README +129 -0
- data/Rakefile +94 -0
- data/lib/atmos/attributes.rb +545 -0
- data/lib/atmos/exceptions.rb +28 -0
- data/lib/atmos/object.rb +263 -0
- data/lib/atmos/parser.rb +110 -0
- data/lib/atmos/request.rb +188 -0
- data/lib/atmos/rest.rb +254 -0
- data/lib/atmos/store.rb +209 -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/request_test.rb +50 -0
- data/test/suite.rb +9 -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 +152 -0
- data/test/test_object_misc.rb +80 -0
- data/test/test_object_read.rb +114 -0
- data/test/test_object_update.rb +58 -0
- data/test/test_store.rb +125 -0
- data/test/test_util.rb +58 -0
- metadata +164 -0
data/COPYING
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# Copyright (c) 2011 EMC Corporation
|
2
|
+
# All Rights Reserved
|
3
|
+
#
|
4
|
+
# This software contains the intellectual property of EMC Corporation
|
5
|
+
# or is licensed to EMC Corporation from third parties. Use of this
|
6
|
+
# software and the intellectual property contained therein is expressly
|
7
|
+
# limited to the terms and conditions of the License Agreement under which
|
8
|
+
# it is provided by or on behalf of EMC.
|
data/README
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
#
|
2
|
+
# = Atmos
|
3
|
+
#
|
4
|
+
# This gem is a Ruby library for EMC's Atmos (http://www.emc.com/atmos) REST API.
|
5
|
+
#
|
6
|
+
# Logging is done via the <tt>Log4r</tt> package. The name of the logger is the string +atmos+,
|
7
|
+
# and the log level is set to <tt>Log4r::FATAL</tt> by default.
|
8
|
+
#
|
9
|
+
# == Getting started
|
10
|
+
#
|
11
|
+
# You'll need this gem and a URL to your Atmos installation.
|
12
|
+
# You'll also need your Atmos authentication credentials.
|
13
|
+
#
|
14
|
+
# == Basics
|
15
|
+
# === XML Parser
|
16
|
+
#
|
17
|
+
# The very first thing is that you have to pick your XML parser. There are two options:
|
18
|
+
# <tt>nokogiri</tt> for performance or <tt>rexml</tt> for only pure ruby requirements.
|
19
|
+
# Setting the parser does the require, so make sure you have the gem you want to use installed.
|
20
|
+
# This gem doesn't require either, since it doesn't know which you plan to use.
|
21
|
+
#
|
22
|
+
# Atmos::Parser::parser = Atmos::Parser::NOKOGIRI
|
23
|
+
# Atmos::Parser::parser = Atmos::Parser::REXML
|
24
|
+
#
|
25
|
+
#
|
26
|
+
# === Datastore
|
27
|
+
#
|
28
|
+
# The Atmos installation you will be connecting to is your datastore. You
|
29
|
+
# can see more detailed information at <tt>Atmos::Store</tt>.
|
30
|
+
#
|
31
|
+
# store = Atmos::Store.new(:url => "https://mgmt.atmosonline.com",
|
32
|
+
# :uid => <your identifier>,
|
33
|
+
# :secret => <your secret>)
|
34
|
+
#
|
35
|
+
# From your store, you can create and retrieve objects. Note that objects
|
36
|
+
# are always retrieved without their data, so a progressive download can
|
37
|
+
# be done.
|
38
|
+
#
|
39
|
+
# obj = store.get(id)
|
40
|
+
# newobj = store.create(options)
|
41
|
+
#
|
42
|
+
# Now that you have a store, you can iterate through all listable tags, object ids
|
43
|
+
# by listable tag, and objects by listable tag. Remember that when an Atmos::Object
|
44
|
+
# is instantiated, it loads all ACLs and Metadata.
|
45
|
+
#
|
46
|
+
# store.each_listable_tag { |tag| }
|
47
|
+
#
|
48
|
+
# store.each_object_id_by_tag(tag) do |id|
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# store.each_object_by_tag(tag) do |obj|
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
#
|
55
|
+
# === Objects
|
56
|
+
# See <tt>Atmos::Object</tt> for more detailed info on the object API.
|
57
|
+
#
|
58
|
+
# ==== Creating Objects
|
59
|
+
#
|
60
|
+
# At the very simplest, you can create an object with nothing. The mimetype
|
61
|
+
# will default to <tt>binary/octet-stream</tt>. The object will be created
|
62
|
+
# with no data, no metadata, with permissions set for only the creator to access.
|
63
|
+
#
|
64
|
+
# obj = store.create()
|
65
|
+
#
|
66
|
+
# You can also create an object with (any combination of) more information
|
67
|
+
# up front. (See <tt>Atmos::Store.create</tt> for more detailed info on
|
68
|
+
# object creation options.)
|
69
|
+
#
|
70
|
+
# obj = store.create(
|
71
|
+
# :user_acl => {'janet' => :full, 'xavier' => :read},
|
72
|
+
# :group_acl => {'other' => :read},
|
73
|
+
# :metadata => {'key' => 'value', 'another_key' => nil},
|
74
|
+
# :listable_metadata => {'lkey' => 'lvalue', 'another_lkey' => nil},
|
75
|
+
# :mimetype => "video/quicktime",
|
76
|
+
# :data => open("local/video_file.qt")
|
77
|
+
# )
|
78
|
+
#
|
79
|
+
# ===== Reading Objects
|
80
|
+
#
|
81
|
+
# You can get the data for an object all at once, which means it all
|
82
|
+
# gets read into memory all at once:
|
83
|
+
#
|
84
|
+
# @data = @obj.data
|
85
|
+
#
|
86
|
+
# Or progressively download it by passing a block to <tt>read</tt>:
|
87
|
+
#
|
88
|
+
# @file = open('mydata', 'w')
|
89
|
+
# @obj.read do |chunk|
|
90
|
+
# @file.write chunk
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
#
|
94
|
+
# ===== Object Metadata
|
95
|
+
#
|
96
|
+
# An object has system and user metadata. User metadata can be modified,
|
97
|
+
# system metadata cannot. User metadata is further divided into listable
|
98
|
+
# and non-listable metadata. Listable metadata means the object is indexed
|
99
|
+
# on the key value of the metadata key/value pair (also known as a
|
100
|
+
# <tt>tag</tt>), and can be retrieved via the tag.
|
101
|
+
#
|
102
|
+
# Set or change user metadata, listable or non-listable, keys and values must be Strings:
|
103
|
+
#
|
104
|
+
# @obj.metadata[newkey] = "newvalue"
|
105
|
+
#
|
106
|
+
# (See <tt>Atmos::Metadata</tt> for more detailed info.)
|
107
|
+
#
|
108
|
+
#
|
109
|
+
# ===== Object ACL
|
110
|
+
#
|
111
|
+
# There are two ACLs on an object: <tt>user_acl</tt> and <tt>group_acl</tt>.
|
112
|
+
# User and group names are ruby Strings, while the values are one of the
|
113
|
+
# following symbols: <tt>:none</tt>, <tt>:read</tt>, <tt>:write</tt> ,<tt>:full</tt>.
|
114
|
+
# They are accessed as Hashes on the object. All normal Hash functions will work.
|
115
|
+
#
|
116
|
+
# When permissions are set to <tt>:none</tt> the pair disappears from the hash.
|
117
|
+
#
|
118
|
+
# @obj.user_acl['user'] = :full
|
119
|
+
# @obj.group_acl['other'] = :none
|
120
|
+
#
|
121
|
+
# (See <tt>Atmos::ACL</tt> for more detailed info.)
|
122
|
+
#
|
123
|
+
#
|
124
|
+
# ===== Other Object Functionality
|
125
|
+
#
|
126
|
+
# @obj.exists?
|
127
|
+
# @obj.delete
|
128
|
+
#
|
129
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rdoc/task'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'test/unit'
|
8
|
+
|
9
|
+
require File.dirname(__FILE__) + '/lib/atmos'
|
10
|
+
|
11
|
+
task :default => :test
|
12
|
+
|
13
|
+
|
14
|
+
desc "run tests"
|
15
|
+
Rake::TestTask.new do |t|
|
16
|
+
libdir = File.expand_path('lib')
|
17
|
+
testdir = File.expand_path('test')
|
18
|
+
|
19
|
+
t.libs = [libdir, testdir]
|
20
|
+
#t.pattern = 'test/lib/test_*.rb'
|
21
|
+
t.verbose = true
|
22
|
+
#t.warning = true
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
Rake::RDocTask.new do |rdoc|
|
27
|
+
rdoc.rdoc_dir = 'doc'
|
28
|
+
rdoc.title = "EMC Atmos' REST API gem - ruby-atmos"
|
29
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--main=README'
|
30
|
+
rdoc.rdoc_files.include('README')
|
31
|
+
rdoc.rdoc_files.include('lib/*.rb')
|
32
|
+
rdoc.rdoc_files.include('lib/atmos/*.rb')
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
spec = Gem::Specification.new do |s|
|
37
|
+
s.platform = Gem::Platform::RUBY
|
38
|
+
s.name = 'ruby-atmos'
|
39
|
+
s.version = Gem::Version.new(Atmos::VERSION)
|
40
|
+
s.summary = "Client library for EMC's Atmos REST API"
|
41
|
+
s.description = "Ruby OO library to access an EMC Atmos server as a blobstore."
|
42
|
+
s.email = 'fleur.dragan@emc.com'
|
43
|
+
s.author = 'Fleur Dragan'
|
44
|
+
s.has_rdoc = true
|
45
|
+
s.extra_rdoc_files = %w(README COPYING)
|
46
|
+
s.homepage = 'http://www.emc.com/atmos'
|
47
|
+
#s.rubyforge_project = 'atmos'
|
48
|
+
s.files = FileList['Rakefile', 'lib/*/*.rb']
|
49
|
+
s.test_files = Dir['test/**/*']
|
50
|
+
s.require_path = 'lib'
|
51
|
+
s.add_dependency('log4r', '>=1.1.9')
|
52
|
+
s.add_dependency('ruby-hmac', '>=0.4.0')
|
53
|
+
|
54
|
+
s.rdoc_options = ['--title', "EMC Atmos' REST API gem - ruby-atmos",
|
55
|
+
'--main', 'README',
|
56
|
+
'--line-numbers', '--inline-source']
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# DRY: set up gemspecs for both versions of gems
|
61
|
+
#
|
62
|
+
nokogiri_spec = spec.clone
|
63
|
+
nokogiri_spec.name = 'ruby-atmos'
|
64
|
+
nokogiri_spec.add_dependency('nokogiri', '>=1.4.4')
|
65
|
+
nokogiri_spec.description = "Ruby OO library to access an EMC Atmos server with nokogiri as XML parser."
|
66
|
+
nokogiri_spec.rubyforge_project = nokogiri_spec.name
|
67
|
+
|
68
|
+
rexml_spec = spec.clone
|
69
|
+
rexml_spec.name = 'ruby-atmos-pure'
|
70
|
+
rexml_spec.files += FileList['lib/atmos/*/rexml.rb']
|
71
|
+
rexml_spec.description = "Ruby OO library to access an EMC Atmos server with rexml as XML parser."
|
72
|
+
rexml_spec.rubyforge_project = rexml_spec.name
|
73
|
+
|
74
|
+
|
75
|
+
#
|
76
|
+
# creating the actual package tasks for the two gems
|
77
|
+
#
|
78
|
+
Rake::GemPackageTask.new(nokogiri_spec) { |pkg| }
|
79
|
+
Rake::GemPackageTask.new(rexml_spec) { |pkg| }
|
80
|
+
|
81
|
+
|
82
|
+
desc 'install gem with dependencies'
|
83
|
+
task :install => :package do
|
84
|
+
sh "sudo gem i pkg/#{spec.name}-#{spec.version}.gem"
|
85
|
+
end
|
86
|
+
|
87
|
+
desc 'uninstall gem'
|
88
|
+
task :uninstall do
|
89
|
+
sh "sudo gem uninstall #{spec.name} -x"
|
90
|
+
end
|
91
|
+
|
92
|
+
desc 'reinstall gem'
|
93
|
+
task :reinstall => [:uninstall, :install]
|
94
|
+
|
@@ -0,0 +1,545 @@
|
|
1
|
+
module Atmos
|
2
|
+
|
3
|
+
#
|
4
|
+
# == AttributeHashBase
|
5
|
+
#
|
6
|
+
# Base class for ACL and Metadata objects. Contains utility methods common to both,
|
7
|
+
# as well as implementations of standard ruby Hash functions to make both ACL and
|
8
|
+
# Metadata objects appear as hashes while interacting with Atmos at the same time.
|
9
|
+
#
|
10
|
+
#
|
11
|
+
class AttributeHashBase < Hash # :nodoc:
|
12
|
+
attr_reader :last_reload_at
|
13
|
+
|
14
|
+
@obj = nil
|
15
|
+
@header = nil
|
16
|
+
@last_reload_at = nil
|
17
|
+
@set_action = nil
|
18
|
+
@reload_action = nil
|
19
|
+
@delete_action = nil
|
20
|
+
|
21
|
+
|
22
|
+
def delete_with_atmos(key)
|
23
|
+
delete_without_atmos(key)
|
24
|
+
end
|
25
|
+
alias :delete_without_atmos :delete
|
26
|
+
alias :delete :delete_with_atmos
|
27
|
+
|
28
|
+
|
29
|
+
def clear_with_atmos
|
30
|
+
clear_without_atmos
|
31
|
+
end
|
32
|
+
alias :clear_without_atmos :clear
|
33
|
+
alias :clear :clear_with_atmos
|
34
|
+
|
35
|
+
|
36
|
+
def replace_with_atmos(h)
|
37
|
+
validate_input_hash(h)
|
38
|
+
replace_without_atmos(h)
|
39
|
+
end
|
40
|
+
alias :replace_without_atmos :replace
|
41
|
+
alias :replace :replace_with_atmos
|
42
|
+
|
43
|
+
|
44
|
+
def merge_with_atmos(h)
|
45
|
+
validate_input_hash(h)
|
46
|
+
merge_without_atmos(h)
|
47
|
+
end
|
48
|
+
alias :merge_without_atmos :merge
|
49
|
+
alias :merge :merge_with_atmos
|
50
|
+
|
51
|
+
|
52
|
+
def merge_with_atmos!(h)
|
53
|
+
validate_input_hash(h)
|
54
|
+
|
55
|
+
input = h.clone
|
56
|
+
input.each do |k,v|
|
57
|
+
input[k] = xlate_value_from_object_to_header(v)
|
58
|
+
end
|
59
|
+
|
60
|
+
response = @obj.request.do(@set_action, :id => @obj.aoid, @header => Atmos::Util.hash2header(input))
|
61
|
+
reload(@reload_action, @obj.aoid)
|
62
|
+
end
|
63
|
+
alias :merge_without_atmos! :merge!
|
64
|
+
alias :merge! :merge_with_atmos!
|
65
|
+
alias :update :merge!
|
66
|
+
|
67
|
+
|
68
|
+
def default_with_atmos=
|
69
|
+
raise Atmos::Exceptions::NotImplementedExceptions, "Not implemented for Atmos Object properties."
|
70
|
+
end
|
71
|
+
alias :default_without_atmos= :default=
|
72
|
+
alias :default= :default_with_atmos=
|
73
|
+
|
74
|
+
|
75
|
+
#
|
76
|
+
# Same as self[key] = value
|
77
|
+
#
|
78
|
+
def store(key, value)
|
79
|
+
self[key] = value
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
#
|
84
|
+
# This method is used internally to generate REST requests to the Atmos server.
|
85
|
+
#
|
86
|
+
# Each ACL or Metadata object knows what HTTP header it is representing
|
87
|
+
# in the Atmos REST API we're wrapping. This method returns that.
|
88
|
+
#
|
89
|
+
def header_name
|
90
|
+
return @header
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
#
|
95
|
+
# This method is used internally to generate REST requests to the Atmos server.
|
96
|
+
#
|
97
|
+
# Returns data as a String in the form of a valid HTTP header Atmos will
|
98
|
+
# recognize.
|
99
|
+
#
|
100
|
+
def header_value
|
101
|
+
Atmos::Util.hash2header(self)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
#
|
106
|
+
# This method is used internally to generate REST requests to the Atmos server.
|
107
|
+
#
|
108
|
+
def to_header
|
109
|
+
"#{self.header_name}: #{self.header_value}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_canonicalized_header
|
113
|
+
self.to_header.squeeze
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
protected
|
118
|
+
def validate_input_hash(h)
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
def xlate_value_from_header_to_object(value)
|
123
|
+
value
|
124
|
+
end
|
125
|
+
|
126
|
+
def reload(action, aoid, options = {})
|
127
|
+
response = @obj.request.do(action, options.merge({:id => aoid}))
|
128
|
+
Atmos::LOG.debug("AttributeHashBase.reload: type: #{@type}; header: #{@header}; value: #{response[@header]}")
|
129
|
+
newval = Atmos::Util.header2hash(response[@header])
|
130
|
+
Atmos::LOG.debug("AttributeHashBase.reload: newval: #{newval.inspect}")
|
131
|
+
if (!newval.nil?)
|
132
|
+
newval.each do |key,val|
|
133
|
+
newval[key] = xlate_value_from_header_to_object(val)
|
134
|
+
end
|
135
|
+
Atmos::LOG.debug("AttributeHashBase.reload: newval: #{newval.inspect}")
|
136
|
+
replace_without_atmos(newval)
|
137
|
+
end
|
138
|
+
Atmos::LOG.debug("AttributeHashBase.reload: self: #{self.inspect}")
|
139
|
+
|
140
|
+
@last_reload_at = Time.now()
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
# == Access Control Lists (ACLs)
|
147
|
+
#
|
148
|
+
# There are two hashes for access control, available as properties on the object: +user_acl+ and +group_acl+.
|
149
|
+
# The keys are the Atmos usernames and the values are one of :+none+, :+read+, :+write+, :+full+. The ACLs
|
150
|
+
# behave like normal Hash objects. All operations are executed against the Atmos server immediately.
|
151
|
+
#
|
152
|
+
# === Defaults
|
153
|
+
# By default, when you create an object, the user you gave as a parameter when instantiating Atmos::Store
|
154
|
+
# has full permissions on the object The default group is +other+. So:
|
155
|
+
#
|
156
|
+
# puts obj.user_acl.inspect => {user => :full}
|
157
|
+
# puts obj.group_acl.inspect => {other => :none}
|
158
|
+
#
|
159
|
+
#
|
160
|
+
# === Adding
|
161
|
+
# Adding permissions for a new user is as easy as adding another hash element:
|
162
|
+
#
|
163
|
+
# obj.user_acl[newuser] = :read
|
164
|
+
#
|
165
|
+
# puts obj.user_acl.inspect => {user => :full, newuser => :read}
|
166
|
+
#
|
167
|
+
#
|
168
|
+
# === Modifying
|
169
|
+
# User and group permissions can be modified by modifying the appropriate key value.
|
170
|
+
# Keep in mind that you CAN be dumb and give up access to your own objects,
|
171
|
+
# even if there is no other user that has access to them.
|
172
|
+
#
|
173
|
+
# obj.user_acl[newuser] = :full
|
174
|
+
# puts obj.user_acl.inspect => {user => :full, newuser => :full}
|
175
|
+
#
|
176
|
+
# obj.group_acl['other'] = :full
|
177
|
+
# puts obj.group_acl.inspect => {other => :full}
|
178
|
+
#
|
179
|
+
#
|
180
|
+
# === Deleting
|
181
|
+
# Remove any permissions for a given user or group, you can either modify existing permissions
|
182
|
+
# to :+none+, or you can delete the user/group name from the appropriate hash. When you do either,
|
183
|
+
# the name disappears entirely from the hash.
|
184
|
+
#
|
185
|
+
# obj.user_acl.delete(newuser)
|
186
|
+
# puts obj.user_acl.inspect => {user => :full}
|
187
|
+
#
|
188
|
+
# obj.user_acl[newuser] = :none
|
189
|
+
# puts obj.user_acl.inspect => {user => :full}
|
190
|
+
#
|
191
|
+
#
|
192
|
+
class ACL < AttributeHashBase
|
193
|
+
USER = 1
|
194
|
+
GROUP = 2
|
195
|
+
|
196
|
+
#
|
197
|
+
# This constructor is only meant for internal use. To get ACLs on an object:
|
198
|
+
#
|
199
|
+
# obj.user_acl => Hash
|
200
|
+
# obj.group_acl => Hash
|
201
|
+
#
|
202
|
+
def initialize(obj, type)
|
203
|
+
raise Atmos::Exceptions::ArgumentException, "The 'obj' parameter cannot be nil." if (obj.nil?)
|
204
|
+
raise Atmos::Exceptions::ArgumentException, "The 'obj' parameter must have an id." if (obj.aoid.nil?)
|
205
|
+
raise Atmos::Exceptions::ArgumentException, "The 'type' parameter must be Atmos::ACL::USER or Atmos::ACL::GROUP." if (![USER, GROUP].include?(type))
|
206
|
+
|
207
|
+
super()
|
208
|
+
|
209
|
+
@obj = obj
|
210
|
+
@type = type
|
211
|
+
|
212
|
+
@header = (@type == USER) ? 'x-emc-useracl' : 'x-emc-groupacl'
|
213
|
+
@delete_action = @set_action = (@type == USER) ? :set_user_acl : :set_group_acl
|
214
|
+
@reload_action = :list_acl
|
215
|
+
|
216
|
+
reload(@reload_action, @obj.aoid)
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
#
|
221
|
+
# Adds or modifies permissions for a user or group.
|
222
|
+
# The change is made on the Atmos server immediately.
|
223
|
+
# Valid values are :+none+, :+read+, :+write+, :+full+.
|
224
|
+
#
|
225
|
+
def []=(key,value)
|
226
|
+
validate_value(value)
|
227
|
+
response = @obj.request.do(@set_action, :id => @obj.aoid, @header => "#{key}=#{xlate_value_from_object_to_header(value)}")
|
228
|
+
reload(@reload_action, @obj.aoid)
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
#
|
233
|
+
# Returns +true+ if this ACL object is representing user ACLs.
|
234
|
+
#
|
235
|
+
def user?
|
236
|
+
@type == USER
|
237
|
+
end
|
238
|
+
|
239
|
+
#
|
240
|
+
# Returns +true+ if this ACL object is representing group ACLs.
|
241
|
+
#
|
242
|
+
def group?
|
243
|
+
@type == GROUP
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
#
|
248
|
+
# Removes permissions for specified user/group name. Update is made on the Atmos server immediately.
|
249
|
+
#
|
250
|
+
def delete(key)
|
251
|
+
response = @obj.request.do(@set_action, :id => @obj.aoid, @header => "#{key}=#{xlate_value_from_object_to_header(:none)}")
|
252
|
+
self.delete_without_atmos(key)
|
253
|
+
reload(@reload_action, @obj.aoid)
|
254
|
+
end
|
255
|
+
|
256
|
+
#
|
257
|
+
# Removes all permissions for all groups, or for all users except the one used to instantiate
|
258
|
+
# the Atmos::Store connection.
|
259
|
+
#
|
260
|
+
def clear
|
261
|
+
# do a reload to make absolutely sure ACL is up to date
|
262
|
+
reload(@reload_action, @obj.aoid)
|
263
|
+
|
264
|
+
values = {}
|
265
|
+
self.each do |k,v|
|
266
|
+
values[k] = xlate_value_from_object_to_header(:none)
|
267
|
+
end
|
268
|
+
values.delete(@obj.user)
|
269
|
+
|
270
|
+
response = @obj.request.do(@set_action, :id => @obj.aoid, @header => Atmos::Util.hash2header(values))
|
271
|
+
reload(@reload_action, @obj.aoid)
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
private
|
276
|
+
def validate_input_hash(h)
|
277
|
+
msg = nil
|
278
|
+
bad_keys = []
|
279
|
+
bad_values = []
|
280
|
+
good_values = [:none, :read, :write, :full]
|
281
|
+
|
282
|
+
h.each do |k,v|
|
283
|
+
bad_keys.push(k) if (k.nil? || !k.kind_of?(String))
|
284
|
+
bad_values.push(v) if (v.nil? || !good_values.include?(v))
|
285
|
+
end
|
286
|
+
|
287
|
+
msg = "The input has was bad: " if (!bad_keys.empty? || !bad_values.empty?)
|
288
|
+
msg += "bad keys: #{bad_keys.inspect} " if (!bad_keys.empty?)
|
289
|
+
msg += "bad values: #{bad_values.inspect}" if (!bad_values.empty?)
|
290
|
+
|
291
|
+
raise Atmos::Exceptions::ArgumentException, msg if (!msg.nil?)
|
292
|
+
end
|
293
|
+
|
294
|
+
def validate_value(value)
|
295
|
+
if (![:none, :read, :write, :full].include?(value))
|
296
|
+
raise Atmos::Exceptions::ArgumentException, "Valid permissions values are :none, :read, :write, :full"
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def xlate_value_from_header_to_object(value)
|
301
|
+
case value
|
302
|
+
when 'NONE'
|
303
|
+
:none
|
304
|
+
when 'READ'
|
305
|
+
:read
|
306
|
+
when 'WRITE'
|
307
|
+
:write
|
308
|
+
when 'FULL_CONTROL'
|
309
|
+
:full
|
310
|
+
else
|
311
|
+
raise Atmos::Exceptions::InternalLibraryException, "Permissions type not recognized: #{value}"
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def xlate_value_from_object_to_header(value)
|
316
|
+
case value
|
317
|
+
when :none
|
318
|
+
'NONE'
|
319
|
+
when :read
|
320
|
+
'READ'
|
321
|
+
when :write
|
322
|
+
'WRITE'
|
323
|
+
when :full
|
324
|
+
'FULL_CONTROL'
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
# == Metadata
|
331
|
+
# In all cases, +metadata+ refers to key/value pairs, and is represented
|
332
|
+
# as a ruby Hash attached to an Atmos::Object object.
|
333
|
+
#
|
334
|
+
# === Standard Metadata
|
335
|
+
# In Atmos, +metadata+ with no modifier (e.g. +listable+ or +system+)
|
336
|
+
# refers to metadata on an object that is not indexed. In other words,
|
337
|
+
# objects cannot be referenced by the metadata information. The only
|
338
|
+
# way to get this metadata information is to already have the object
|
339
|
+
# the metadata is attached to.
|
340
|
+
#
|
341
|
+
# ==== Defaults
|
342
|
+
# By default, when you create an object, there is no metadata. So:
|
343
|
+
#
|
344
|
+
# obj.metadata => {}
|
345
|
+
#
|
346
|
+
# ==== Adding
|
347
|
+
# Add metadata in key/value pairs as you would to a hash. Keys and
|
348
|
+
# values must be ruby Strings.
|
349
|
+
#
|
350
|
+
# obj.metadata[key] = value
|
351
|
+
# obj.metadata.store(key, value)
|
352
|
+
# obj.metadata.merge!({key => value})
|
353
|
+
#
|
354
|
+
# ==== Modifying
|
355
|
+
# Modify metadata as you would a hash. Keys and values must be ruby
|
356
|
+
# Strings.
|
357
|
+
#
|
358
|
+
# obj.metadata[pre-existing-key] = new-value
|
359
|
+
# obj.metadata.store(pre-existing-key, new-value)
|
360
|
+
# obj.metadata.merge!({pre-existing-key => new-value})
|
361
|
+
#
|
362
|
+
# ==== Deleting
|
363
|
+
# Delete metadata as you would a hash. Keys must be ruby
|
364
|
+
# Strings.
|
365
|
+
#
|
366
|
+
# obj.metadata.delete(key)
|
367
|
+
# obj.metadata.clear
|
368
|
+
#
|
369
|
+
# === Listable Metadata
|
370
|
+
# Listable metadata means atmos indexes the object by the key in the
|
371
|
+
# key/value metadata pair. The keys of listable metadata are also known
|
372
|
+
# as listable tags. Currently, an Atmos server can handle about 10k listable
|
373
|
+
# tags easily, so use judiciously with very large datasets.
|
374
|
+
#
|
375
|
+
# ==== Defaults
|
376
|
+
# By default, when you create an object, there is no listable metadata. So:
|
377
|
+
#
|
378
|
+
# obj.listable_metadata => {}
|
379
|
+
#
|
380
|
+
# ==== Adding
|
381
|
+
# Add metadata in key/value pairs as you would to a hash. Keys and
|
382
|
+
# values must be ruby Strings.
|
383
|
+
#
|
384
|
+
# obj.listable_metadata[key] = value
|
385
|
+
# obj.listable_metadata.store(key, value)
|
386
|
+
# obj.listable_metadata.merge!({key => value})
|
387
|
+
#
|
388
|
+
# ==== Modifying
|
389
|
+
# Modify metadata as you would a hash. Keys and values must be ruby
|
390
|
+
# Strings.
|
391
|
+
#
|
392
|
+
# obj.listable_metadata[pre-existing-key] = new-value
|
393
|
+
# obj.listable_metadata.store(pre-existing-key, new-value)
|
394
|
+
# obj.listable_metadata.merge!({pre-existing-key => new-value})
|
395
|
+
#
|
396
|
+
# ==== Deleting
|
397
|
+
# Delete metadata as you would a hash. Keys must be ruby
|
398
|
+
# Strings.
|
399
|
+
#
|
400
|
+
# obj.listable_metadata.delete(key)
|
401
|
+
# obj.listable_metadata.clear
|
402
|
+
#
|
403
|
+
# === System Metadata
|
404
|
+
# System metadata is a standard group of information maintained by Atmos for each object,
|
405
|
+
# such as +atime+, +mtime+, +type+, +policyname+.
|
406
|
+
#
|
407
|
+
# System metadata is not modifiable.
|
408
|
+
#
|
409
|
+
class Metadata < AttributeHashBase
|
410
|
+
LISTABLE = 1
|
411
|
+
NON_LISTABLE = 2
|
412
|
+
SYSTEM = 3
|
413
|
+
|
414
|
+
|
415
|
+
#
|
416
|
+
# This constructor is only meant for internal use. To get the metadata on an object:
|
417
|
+
#
|
418
|
+
# obj.metadata => Hash
|
419
|
+
# obj.listable_metadata => Hash
|
420
|
+
# obj.system_metadata => Hash
|
421
|
+
#
|
422
|
+
def initialize(obj, type)
|
423
|
+
raise Atmos::Exceptions::ArgumentException, "The 'obj' parameter cannot be nil." if (obj.nil?)
|
424
|
+
raise Atmos::Exceptions::ArgumentException, "The 'obj' parameter must have an id." if (obj.aoid.nil?)
|
425
|
+
raise Atmos::Exceptions::ArgumentException, "The 'type' parameter must be one of Atmos::Metadata::LISTABLE Atmos::Metadata::NON_LISTABLE Atmos::Metadata::SYSTEM." if (![SYSTEM, LISTABLE, NON_LISTABLE].include?(type))
|
426
|
+
|
427
|
+
super()
|
428
|
+
|
429
|
+
@obj = obj
|
430
|
+
@type = type
|
431
|
+
|
432
|
+
@header = (@type == LISTABLE) ? 'x-emc-listable-meta' : 'x-emc-meta'
|
433
|
+
@reload_action = (@type == SYSTEM) ? :list_system_metadata : :list_metadata
|
434
|
+
@set_action = (@type == LISTABLE) ? :set_listable_metadata : :set_metadata
|
435
|
+
|
436
|
+
reload(@reload_action, @obj.aoid)
|
437
|
+
end
|
438
|
+
|
439
|
+
|
440
|
+
#
|
441
|
+
# Adds the specified metadata to the object unless the object is representing system metadata.
|
442
|
+
#
|
443
|
+
# System metadata cannot be modified, so an Atmos::Exceptions::NotImplementedException
|
444
|
+
# is thrown.
|
445
|
+
#
|
446
|
+
# The change is made on the Atmos server immediately.
|
447
|
+
#
|
448
|
+
def []=(key,value)
|
449
|
+
raise Atmos::Exceptions::NotImplementedException, "System metadata cannot be modified." if (@type == SYSTEM)
|
450
|
+
raise Atmos::Exceptions::ArgumentException, "The 'value' parameter must be of type String'." if (!value.nil? && !value.kind_of?(String))
|
451
|
+
response = @obj.request.do(@set_action, :id => @obj.aoid, @header => "#{key}=#{value}")
|
452
|
+
reload(@reload_action, @obj.aoid)
|
453
|
+
end
|
454
|
+
|
455
|
+
|
456
|
+
#
|
457
|
+
# Deletes all metadata from the object unless the object is representing system metadata.
|
458
|
+
#
|
459
|
+
# System metadata cannot be modified, so an Atmos::Exceptions::NotImplementedException
|
460
|
+
# is thrown.
|
461
|
+
#
|
462
|
+
# The change is made on the Atmos server immediately.
|
463
|
+
#
|
464
|
+
def clear
|
465
|
+
raise Atmos::Exceptions::NotImplementedException, "System metadata cannot be modified." if (@type == SYSTEM)
|
466
|
+
reload(@reload_action, @obj.aoid)
|
467
|
+
response = @obj.request.do(:delete_metadata, :id => @obj.aoid, 'x-emc-tags' => self.keys.join(','))
|
468
|
+
self.clear_without_atmos
|
469
|
+
reload(@reload_action, @obj.aoid)
|
470
|
+
end
|
471
|
+
|
472
|
+
|
473
|
+
#
|
474
|
+
# Deletes the specified metadata from the object unless the
|
475
|
+
# object is representing system metadata. System metadata cannot
|
476
|
+
# be modified, so an <tt>Atmos::Exceptions::NotImplementedException</tt>
|
477
|
+
# is thrown.
|
478
|
+
#
|
479
|
+
# The deleted is executed on the Atmos server immediately.
|
480
|
+
#
|
481
|
+
# Required:
|
482
|
+
# *<tt>key</tt> -
|
483
|
+
def delete(key)
|
484
|
+
raise Atmos::Exceptions::NotImplementedException, "System metadata cannot be modified." if (@type == SYSTEM)
|
485
|
+
response = @obj.request.do(:delete_metadata, :id => @obj.aoid, 'x-emc-tags' => key)
|
486
|
+
self.delete_without_atmos(key)
|
487
|
+
reload(@reload_action, @obj.aoid)
|
488
|
+
end
|
489
|
+
|
490
|
+
|
491
|
+
#
|
492
|
+
# Returns +true+ if this Metadata object is representing listable metadata.
|
493
|
+
#
|
494
|
+
def listable?
|
495
|
+
@type == LISTABLE
|
496
|
+
end
|
497
|
+
|
498
|
+
|
499
|
+
#
|
500
|
+
# Returns +true+ if this Metadata object is representing non-listable metadata.
|
501
|
+
#
|
502
|
+
def non_listable?
|
503
|
+
@type == NON_LISTABLE
|
504
|
+
end
|
505
|
+
|
506
|
+
|
507
|
+
#
|
508
|
+
# Returns +true+ if this Metadata object is representing system metadata.
|
509
|
+
#
|
510
|
+
def system?
|
511
|
+
@type == SYSTEM
|
512
|
+
end
|
513
|
+
|
514
|
+
|
515
|
+
private
|
516
|
+
#
|
517
|
+
# This method is defined to override AttributeHashBase.validate_input_hash,
|
518
|
+
# which simply returns true. This allows the base class to contain the
|
519
|
+
# method definitions for several standard Hash functions, yet allows the
|
520
|
+
# validation to be specialized for each subclass.
|
521
|
+
#
|
522
|
+
# Required:
|
523
|
+
# *<tt>h</tt> - the hash to validate
|
524
|
+
#
|
525
|
+
def validate_input_hash(h)
|
526
|
+
msg = nil
|
527
|
+
bad_keys = []
|
528
|
+
bad_values = []
|
529
|
+
good_values = [:none, :read, :write, :full]
|
530
|
+
|
531
|
+
h.each do |k,v|
|
532
|
+
bad_keys.push(k) if (k.nil? || !k.kind_of?(String))
|
533
|
+
bad_values.push(v) if (v.nil? || !good_values.include?(v))
|
534
|
+
end
|
535
|
+
|
536
|
+
msg = "The input has was bad: " if (!bad_keys.empty? || !bad_values.empty?)
|
537
|
+
msg += "bad keys: #{bad_keys.inspect} " if (!bad_keys.empty?)
|
538
|
+
msg += "bad values: #{bad_values.inspect}" if (!bad_values.empty?)
|
539
|
+
|
540
|
+
raise Atmos::Exceptions::ArgumentException, msg if (!msg.nil?)
|
541
|
+
end
|
542
|
+
|
543
|
+
end
|
544
|
+
|
545
|
+
end
|