Branching mogilefs-client to version 1.2.1
[ruby-mogilefs-client.git] / lib / mogilefs / mogilefs.rb
blobb6776cefecdb7a9822c4cb2775ce6d6c226936d4
1 require 'open-uri'
2 require 'timeout'
4 require 'mogilefs/client'
5 require 'mogilefs/nfsfile'
7 ##
8 # Timeout error class.
10 class MogileFS::Timeout < Timeout::Error; end
13 # MogileFS File manipulation client.
15 class MogileFS::MogileFS < MogileFS::Client
17   ##
18   # The path to the local MogileFS mount point if you are using NFS mode.
20   attr_reader :root
22   ##
23   # The domain of keys for this MogileFS client.
25   attr_reader :domain
27   ##
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]
34     @root = args[:root]
36     raise ArgumentError, "you must specify a domain" unless @domain
38     super
39   end
41   ##
42   # Enumerates keys starting with +key+.
44   def each_key(prefix)
45     after = nil
47     keys, after = list_keys prefix
49     until keys.empty? do
50       keys.each { |k| yield k }
51       keys, after = list_keys prefix, after
52     end
54     return nil
55   end
57   ##
58   # Retrieves the contents of +key+.
60   def get_file_data(key)
61     paths = get_paths key
63     return nil unless paths
65     paths.each do |path|
66       next unless path
67       case path
68       when /^http:\/\// then
69         begin
70           path = URI.parse path
71           data = timeout(5, MogileFS::Timeout) { path.read }
72           return data
73         rescue MogileFS::Timeout
74           next
75         end
76       else
77         next unless File.exist? path
78         return File.read(path)
79       end
80     end
82     return nil
83   end
85   ##
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)
92     
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 }
98   end
100   ##
101   # Creates a new file +key+ in +klass+.  +bytes+ is currently unused.
102   #
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
113     dests = nil
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}"]]
118       end
119     else
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']]]
127     end
129     dest = dests.first
130     devid, path = dest
132     case path
133     when /^http:\/\// then
134       MogileFS::HTTPFile.open(self, res['fid'], path, devid, klass, key,
135                               dests, bytes, &block)
136     else
137       MogileFS::NFSFile.open(self, res['fid'], path, devid, klass, key, &block)
138     end
139   end
141   ##
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)
151       else
152         return File.open(file) { |fp| copy(fp, mfp) }
153       end
154     end
155   end
157   ##
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|
164       mfp << content
165     end
167     return content.length
168   end
170   ##
171   # Removes +key+.
173   def delete(key)
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}"
180     end
181   end
183   ##
184   # Sleeps +duration+.
186   def sleep(duration)
187     @backend.sleep :duration => duration
188   end
190   ##
191   # Renames a key +from+ to key +to+.
193   def rename(from, 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}"
200     end
201   end
203   ##
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']
216   end
218   private
220   def copy(from, to) # HACK use FileUtils
221     bytes = 0
223     until from.eof? do
224       chunk = from.read 8192
225       to.write chunk
226       bytes += chunk.length
227     end
229     return bytes
230   end