4 require 'mogilefs/client'
5 require 'mogilefs/nfsfile'
10 class MogileFS::Timeout < Timeout::Error; end
13 # MogileFS File manipulation client.
15 class MogileFS::MogileFS < MogileFS::Client
18 # The path to the local MogileFS mount point if you are using NFS mode.
23 # The domain of keys for this MogileFS client.
28 # Creates a new MogileFS::MogileFS instance. +args+ must include a key
29 # :domain specifying the domain of this client. A key :root will be used to
30 # specify the root of the NFS file system.
32 def initialize(args = {})
33 @domain = args[:domain]
36 raise ArgumentError, "you must specify a domain" unless @domain
42 # Enumerates keys starting with +key+.
47 keys, after = list_keys prefix
50 keys.each { |k| yield k }
51 keys, after = list_keys prefix, after
58 # Retrieves the contents of +key+.
60 def get_file_data(key)
63 return nil unless paths
68 when /^http:\/\// then
71 data = timeout(5, MogileFS::Timeout) { path.read }
73 rescue MogileFS::Timeout
77 next unless File.exist? path
78 return File.read(path)
86 # Get the paths for +key+.
88 def get_paths(key, noverify = true, zone = nil)
89 noverify = noverify ? 1 : 0
90 res = @backend.get_paths(:domain => @domain, :key => key,
91 :noverify => noverify, :zone => zone)
93 return nil if res.nil? and @backend.lasterr == 'unknown_key'
94 paths = (1..res['paths'].to_i).map { |i| res["path#{i}"] }
95 return paths if paths.empty?
96 return paths if paths.first =~ /^http:\/\//
97 return paths.map { |path| File.join @root, path }
101 # Creates a new file +key+ in +klass+. +bytes+ is currently unused.
103 # The +block+ operates like File.open.
105 def new_file(key, klass, bytes = 0, &block) # :yields: file
106 raise 'readonly mogilefs' if readonly?
108 res = @backend.create_open(:domain => @domain, :class => klass,
109 :key => key, :multi_dest => 1)
111 raise "#{@backend.lasterr}: #{@backend.lasterrstr}" if res.nil? # HACK
115 if res.include? 'dev_count' then # HACK HUH?
116 dests = (1..res['dev_count'].to_i).map do |i|
117 [res["devid_#{i}"], res["path_#{i}"]]
120 # 0x0040: d0e4 4f4b 2064 6576 6964 3d31 2666 6964 ..OK.devid=1&fid
121 # 0x0050: 3d33 2670 6174 683d 6874 7470 3a2f 2f31 =3&path=http://1
122 # 0x0060: 3932 2e31 3638 2e31 2e37 323a 3735 3030 92.168.1.72:7500
123 # 0x0070: 2f64 6576 312f 302f 3030 302f 3030 302f /dev1/0/000/000/
124 # 0x0080: 3030 3030 3030 3030 3033 2e66 6964 0d0a 0000000003.fid..
126 dests = [[res['devid'], res['path']]]
133 when /^http:\/\// then
134 MogileFS::HTTPFile.open(self, res['fid'], path, devid, klass, key,
135 dests, bytes, &block)
137 MogileFS::NFSFile.open(self, res['fid'], path, devid, klass, key, &block)
142 # Copies the contents of +file+ into +key+ in class +klass+. +file+ can be
143 # either a file name or an object that responds to #read.
145 def store_file(key, klass, file)
146 raise 'readonly mogilefs' if readonly?
148 new_file key, klass do |mfp|
149 if file.respond_to? :read then
150 return copy(file, mfp)
152 return File.open(file) { |fp| copy(fp, mfp) }
158 # Stores +content+ into +key+ in class +klass+.
160 def store_content(key, klass, content)
161 raise 'readonly mogilefs' if readonly?
163 new_file key, klass do |mfp|
167 return content.length
174 raise 'readonly mogilefs' if readonly?
176 res = @backend.delete :domain => @domain, :key => key
178 if res.nil? and @backend.lasterr != 'unknown_key' then
179 raise "unable to delete #{key}: #{@backend.lasterr}"
187 @backend.sleep :duration => duration
191 # Renames a key +from+ to key +to+.
194 raise 'readonly mogilefs' if readonly?
196 res = @backend.rename :domain => @domain, :from_key => from, :to_key => to
198 if res.nil? and @backend.lasterr != 'unknown_key' then
199 raise "unable to rename #{from_key} to #{to_key}: #{@backend.lasterr}"
204 # Lists keys starting with +prefix+ follwing +after+ up to +limit+. If
205 # +after+ is nil the list starts at the beginning.
207 def list_keys(prefix, after = nil, limit = 1000)
208 res = @backend.list_keys(:domain => domain, :prefix => prefix,
209 :after => after, :limit => limit)
211 return nil if res.nil?
213 keys = (1..res['key_count'].to_i).map { |i| res["key_#{i}"] }
215 return keys, res['next_after']
220 def copy(from, to) # HACK use FileUtils
224 chunk = from.read 8192
226 bytes += chunk.length