mogilefs-client 2.2.0 → 3.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +11 -0
- data/.gemtest +0 -0
- data/.gitignore +4 -0
- data/.wrongdoc.yml +5 -0
- data/GIT-VERSION-GEN +28 -0
- data/GNUmakefile +44 -0
- data/HACKING +33 -0
- data/{History.txt → History} +0 -1
- data/{LICENSE.txt → LICENSE} +0 -1
- data/Manifest.txt +34 -7
- data/README +51 -0
- data/Rakefile +11 -11
- data/TODO +10 -0
- data/bin/mog +109 -68
- data/examples/mogstored_rack.rb +189 -0
- data/lib/mogilefs.rb +56 -17
- data/lib/mogilefs/admin.rb +128 -62
- data/lib/mogilefs/backend.rb +205 -95
- data/lib/mogilefs/bigfile.rb +54 -70
- data/lib/mogilefs/bigfile/filter.rb +58 -0
- data/lib/mogilefs/chunker.rb +30 -0
- data/lib/mogilefs/client.rb +0 -2
- data/lib/mogilefs/copy_stream.rb +30 -0
- data/lib/mogilefs/http_file.rb +175 -0
- data/lib/mogilefs/http_reader.rb +79 -0
- data/lib/mogilefs/mogilefs.rb +242 -148
- data/lib/mogilefs/mysql.rb +3 -4
- data/lib/mogilefs/paths_size.rb +24 -0
- data/lib/mogilefs/pool.rb +0 -1
- data/lib/mogilefs/socket.rb +9 -0
- data/lib/mogilefs/socket/kgio.rb +55 -0
- data/lib/mogilefs/socket/pure_ruby.rb +70 -0
- data/lib/mogilefs/socket_common.rb +58 -0
- data/lib/mogilefs/util.rb +6 -169
- data/test/aggregate.rb +11 -11
- data/test/exec.rb +72 -0
- data/test/fresh.rb +222 -0
- data/test/integration.rb +43 -0
- data/test/setup.rb +1 -0
- data/test/socket_test.rb +98 -0
- data/test/test_admin.rb +14 -37
- data/test/test_backend.rb +50 -107
- data/test/test_bigfile.rb +2 -2
- data/test/test_db_backend.rb +1 -2
- data/test/test_fresh.rb +8 -0
- data/test/test_http_reader.rb +34 -0
- data/test/test_mogilefs.rb +278 -98
- data/test/test_mogilefs_integration.rb +174 -0
- data/test/test_mogilefs_integration_large_pipe.rb +62 -0
- data/test/test_mogilefs_integration_list_keys.rb +40 -0
- data/test/test_mogilefs_socket_kgio.rb +11 -0
- data/test/test_mogilefs_socket_pure.rb +7 -0
- data/test/test_mogstored_rack.rb +89 -0
- data/test/test_mogtool_bigfile.rb +116 -0
- data/test/test_mysql.rb +1 -2
- data/test/test_pool.rb +1 -1
- data/test/test_unit_mogstored_rack.rb +72 -0
- metadata +76 -54
- data/README.txt +0 -80
- data/lib/mogilefs/httpfile.rb +0 -157
- data/lib/mogilefs/network.rb +0 -107
- data/test/test_network.rb +0 -56
- data/test/test_util.rb +0 -121
@@ -0,0 +1,189 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'tempfile'
|
3
|
+
require 'digest/md5'
|
4
|
+
require 'rack'
|
5
|
+
|
6
|
+
# Rack application for handling HTTP PUT/DELETE/MKCOL operations needed
|
7
|
+
# for a MogileFS storage server. GET requests are handled by
|
8
|
+
# Rack::File and Rack::Head _must_ be in the middleware stack for
|
9
|
+
# mogilefsd fsck to work properly with keepalive.
|
10
|
+
#
|
11
|
+
# Usage in rackup config file (config.ru):
|
12
|
+
#
|
13
|
+
# require "./mogstored_rack"
|
14
|
+
# use Rack::Head
|
15
|
+
# run MogstoredRack.new("/var/mogdata")
|
16
|
+
class MogstoredRack
|
17
|
+
class ContentMD5 < Digest::MD5
|
18
|
+
def content_md5
|
19
|
+
[ digest ].pack("m").strip!
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(root, opts = {})
|
24
|
+
@root = File.expand_path(root)
|
25
|
+
@rack_file = (opts[:app] || Rack::File.new(@root))
|
26
|
+
@fsync = !! opts[:fsync]
|
27
|
+
@creat_perms = opts[:creat_perms] || (~File.umask & 0666)
|
28
|
+
@mkdir_perms = opts[:mkdir_perms] || (~File.umask & 0777)
|
29
|
+
@reread_verify = !! opts[:reread_verify]
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
case env["REQUEST_METHOD"]
|
34
|
+
when "GET", "HEAD"
|
35
|
+
case env["PATH_INFO"]
|
36
|
+
when "/"
|
37
|
+
r(200, "") # MogileFS seems to need this...
|
38
|
+
else
|
39
|
+
@rack_file.call(env)
|
40
|
+
end
|
41
|
+
when "PUT"
|
42
|
+
put(env)
|
43
|
+
when "DELETE"
|
44
|
+
delete(env)
|
45
|
+
when "MKCOL"
|
46
|
+
mkcol(env)
|
47
|
+
else
|
48
|
+
r(405, "unsupported method", env)
|
49
|
+
end
|
50
|
+
rescue Errno::EPERM, Errno::EACCES => err
|
51
|
+
r(403, "#{err.message} (#{err.class})", env)
|
52
|
+
rescue => err
|
53
|
+
r(500, "#{err.message} (#{err.class})", env)
|
54
|
+
end
|
55
|
+
|
56
|
+
def mkcol(env)
|
57
|
+
path = server_path(env) or return r(400)
|
58
|
+
Dir.mkdir(path, @mkdir_perms)
|
59
|
+
r(204)
|
60
|
+
rescue Errno::EEXIST # succeed (204) on race condition
|
61
|
+
File.directory?(path) ? r(204) : r(409)
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete(env)
|
65
|
+
path = server_path(env) or return r(400)
|
66
|
+
File.exist?(path) or return r(404)
|
67
|
+
File.directory?(path) ? Dir.rmdir(path) : File.unlink(path)
|
68
|
+
r(204)
|
69
|
+
rescue Errno::ENOENT # return 404 on race condition
|
70
|
+
File.exist?(path) ? r(500) : r(404)
|
71
|
+
end
|
72
|
+
|
73
|
+
def put(env)
|
74
|
+
path = server_path(env) or return r(400)
|
75
|
+
dir = File.dirname(path)
|
76
|
+
File.directory?(dir) or return r(403)
|
77
|
+
|
78
|
+
Tempfile.open([File.basename(path), ".tmp"], dir) do |tmp|
|
79
|
+
tmp = tmp.to_io # delegated method calls are slower
|
80
|
+
tmp.sync = true
|
81
|
+
tmp.binmode
|
82
|
+
buf = ""
|
83
|
+
received = put_loop(env["rack.input"], tmp, buf)
|
84
|
+
err = content_md5_fail?(env, received) and return err
|
85
|
+
if @reread_verify && err = reread_md5_fail?(env, tmp, received, buf)
|
86
|
+
return err
|
87
|
+
end
|
88
|
+
tmp.chmod(@creat_perms)
|
89
|
+
begin
|
90
|
+
File.link(tmp.path, path)
|
91
|
+
rescue Errno::EEXIST
|
92
|
+
err = rename_overwrite_fail?(tmp.path, path) and return err
|
93
|
+
end
|
94
|
+
fsync(dir, tmp) if @fsync
|
95
|
+
resp = r(201)
|
96
|
+
resp[1]["X-Received-Content-MD5"] = received
|
97
|
+
return resp
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def put_loop(src, dst, buf)
|
102
|
+
md5 = ContentMD5.new
|
103
|
+
while src.read(0x4000, buf)
|
104
|
+
md5.update(buf)
|
105
|
+
dst.write(buf)
|
106
|
+
end
|
107
|
+
md5.content_md5
|
108
|
+
end
|
109
|
+
|
110
|
+
def server_path(env)
|
111
|
+
path = env['PATH_INFO'].squeeze('/')
|
112
|
+
path.split(%r{/}).include?("..") and return false
|
113
|
+
"#@root#{path}"
|
114
|
+
end
|
115
|
+
|
116
|
+
# returns a plain-text HTTP response
|
117
|
+
def r(code, msg = nil, env = nil)
|
118
|
+
if env && logger = env["rack.logger"]
|
119
|
+
logger.warn("#{env['REQUEST_METHOD']} #{env['PATH_INFO']} " \
|
120
|
+
"#{code} #{msg.inspect}")
|
121
|
+
end
|
122
|
+
if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(code)
|
123
|
+
[ code, {}, [] ]
|
124
|
+
else
|
125
|
+
msg ||= Rack::Utils::HTTP_STATUS_CODES[code] || ""
|
126
|
+
msg += "\n" if msg.size > 0
|
127
|
+
[ code,
|
128
|
+
{ 'Content-Type' => 'text/plain', 'Content-Length' => msg.size.to_s },
|
129
|
+
[ msg ] ]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Tries to detect filesystem/disk corruption.
|
134
|
+
# Unfortunately, posix_fadvise(2)/IO#advise is only advisory and
|
135
|
+
# can't guarantee we're not just reading the data in the kernel
|
136
|
+
# page cache.
|
137
|
+
def reread_md5_fail?(env, tmp, received, buf)
|
138
|
+
# try to force a reread from the storage device, not cache
|
139
|
+
tmp.fsync
|
140
|
+
tmp.rewind
|
141
|
+
tmp.advise(:dontneed) rescue nil # only in Ruby 1.9.3 and only advisory
|
142
|
+
|
143
|
+
md5 = ContentMD5.new
|
144
|
+
while tmp.read(0x4000, buf)
|
145
|
+
md5.update(buf)
|
146
|
+
end
|
147
|
+
reread = md5.content_md5
|
148
|
+
reread == received and return false # success
|
149
|
+
r(500, "reread MD5 mismatch\n" \
|
150
|
+
"received: #{received}\n" \
|
151
|
+
" reread: #{reread}", env)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Tries to detect network corruption by verifying the client-supplied
|
155
|
+
# Content-MD5 is correct. It's highly unlikely the MD5 can be corrupted
|
156
|
+
# in a way that also allows corrupt data to pass through.
|
157
|
+
#
|
158
|
+
# The Rainbows!/Unicorn HTTP servers will populate the HTTP_CONTENT_MD5
|
159
|
+
# field in +env+ after env["rack.input"] is fully-consumed. Clients
|
160
|
+
# may also send Content-MD5 as a header and this will still work.
|
161
|
+
def content_md5_fail?(env, received)
|
162
|
+
expected = env["HTTP_CONTENT_MD5"] or return false
|
163
|
+
expected = expected.strip
|
164
|
+
expected == received and return false # success
|
165
|
+
r(400, "Content-MD5 mismatch\n" \
|
166
|
+
"expected: #{expected}\n" \
|
167
|
+
"received: #{received}", env)
|
168
|
+
end
|
169
|
+
|
170
|
+
def rename_overwrite_fail?(src, dst)
|
171
|
+
10.times do
|
172
|
+
begin
|
173
|
+
tmp_dst = "#{dst}.#{rand}"
|
174
|
+
File.link(src, tmp_dst)
|
175
|
+
rescue Errno::EEXIST
|
176
|
+
next
|
177
|
+
end
|
178
|
+
File.rename(tmp_dst, dst)
|
179
|
+
return false # success!
|
180
|
+
end
|
181
|
+
r(409)
|
182
|
+
end
|
183
|
+
|
184
|
+
# fsync each and every directory component above us on the same device
|
185
|
+
def fsync(dir, tmp)
|
186
|
+
tmp.fsync
|
187
|
+
File.open(dir) { |io| io.fsync }
|
188
|
+
end
|
189
|
+
end
|
data/lib/mogilefs.rb
CHANGED
@@ -1,41 +1,80 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
##
|
3
|
-
# MogileFS is a Ruby client for Danga Interactive's open source distributed
|
4
|
-
# filesystem.
|
5
2
|
#
|
6
|
-
# To read more about
|
7
|
-
|
3
|
+
# To read more about \MogileFS, go to http://mogilefs.org/
|
4
|
+
#
|
5
|
+
# Client usage information is available in MogileFS::MogileFS.
|
8
6
|
module MogileFS
|
9
7
|
|
10
|
-
|
8
|
+
# Standard error class for most MogileFS-specific errors
|
9
|
+
class Error < StandardError; end
|
11
10
|
|
12
|
-
##
|
13
11
|
# Raised when a socket remains unreadable for too long.
|
14
|
-
|
15
|
-
class Error < StandardError; end
|
16
12
|
class UnreadableSocketError < Error; end
|
13
|
+
|
14
|
+
# Raised when a response is truncated while reading
|
15
|
+
# due to network/server # errors)
|
17
16
|
class SizeMismatchError < Error; end
|
17
|
+
|
18
|
+
# Raised when checksum verification fails (only while reading deprecated
|
19
|
+
# "bigfiles" from the deprecated mogtool(1).
|
18
20
|
class ChecksumMismatchError < RuntimeError; end
|
21
|
+
|
22
|
+
# Raised when a backend is in read-only mode
|
19
23
|
class ReadOnlyError < Error
|
20
24
|
def message; 'readonly mogilefs'; end
|
21
25
|
end
|
22
|
-
class EmptyPathError < Error
|
23
|
-
def message; 'Empty path for mogile upload'; end
|
24
|
-
end
|
25
26
|
|
27
|
+
# Raised when an upload is impossible
|
28
|
+
class EmptyPathError < Error; end
|
29
|
+
|
30
|
+
# Raised when we are given an unsupported protocol to upload to.
|
31
|
+
# Currently, the \MogileFS (2.55) server only supports HTTP and
|
32
|
+
# this library is only capable of HTTP.
|
26
33
|
class UnsupportedPathError < Error; end
|
34
|
+
|
35
|
+
# Raised when a request (HTTP or tracker) was truncated due to a network or
|
36
|
+
# server error. It may be possible to retry idempotent requests from this.
|
27
37
|
class RequestTruncatedError < Error; end
|
38
|
+
|
39
|
+
# Raised when a response from a server (HTTP or tracker) is not in a format
|
40
|
+
# that we expected (or truncated..
|
28
41
|
class InvalidResponseError < Error; end
|
29
|
-
|
30
|
-
|
42
|
+
|
43
|
+
# Raised when all known backends have failed.
|
44
|
+
class UnreachableBackendError < Error; end
|
45
|
+
|
46
|
+
# There was an error as a result of the use of the (experimental)
|
47
|
+
# pipelining code to the tracker backend
|
48
|
+
class PipelineError < Error; end
|
49
|
+
|
50
|
+
# :stopdoc:
|
51
|
+
class << self
|
52
|
+
# somebody could use IO::Splice from the "io_splice" RubyGem, too
|
53
|
+
# don't consider this a stable API, though...
|
54
|
+
attr_accessor :io
|
55
|
+
end
|
56
|
+
|
57
|
+
# IO.copy_stream was buggy in Ruby 1.9.2 and earlier
|
58
|
+
if RUBY_VERSION >= "1.9.3"
|
59
|
+
@io = IO
|
60
|
+
else
|
61
|
+
require "mogilefs/copy_stream"
|
62
|
+
@io = MogileFS::CopyStream
|
31
63
|
end
|
32
64
|
|
65
|
+
# autoload rarely-used things:
|
66
|
+
autoload :Mysql, 'mogilefs/mysql'
|
67
|
+
autoload :Pool, 'mogilefs/pool'
|
68
|
+
autoload :Admin, 'mogilefs/admin'
|
69
|
+
# :startdoc:
|
33
70
|
end
|
34
71
|
|
72
|
+
require 'mogilefs/util'
|
73
|
+
require 'mogilefs/socket'
|
35
74
|
require 'mogilefs/backend'
|
36
|
-
require 'mogilefs/
|
75
|
+
require 'mogilefs/http_file'
|
76
|
+
require 'mogilefs/http_reader'
|
37
77
|
require 'mogilefs/client'
|
38
78
|
require 'mogilefs/bigfile'
|
39
79
|
require 'mogilefs/mogilefs'
|
40
|
-
require 'mogilefs/
|
41
|
-
|
80
|
+
require 'mogilefs/version' # generated by ./GIT-VERSION-GEN
|
data/lib/mogilefs/admin.rb
CHANGED
@@ -1,25 +1,22 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
require 'mogilefs/client'
|
3
|
-
|
4
|
-
##
|
5
|
-
# A MogileFS Administration Client
|
6
2
|
|
3
|
+
# \MogileFS administration client, this has little real-world usage
|
4
|
+
# and is considered a work-in-progress
|
7
5
|
class MogileFS::Admin < MogileFS::Client
|
8
6
|
|
9
7
|
##
|
10
|
-
# Enumerates fids using #list_fids.
|
8
|
+
# Enumerates fids using #list_fids. Returns the number of valid fids
|
9
|
+
# processed
|
11
10
|
|
12
11
|
def each_fid
|
13
|
-
low =
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
0.step max, 100 do |high|
|
19
|
-
fids = list_fids low, high
|
12
|
+
low = -1
|
13
|
+
rv = 0
|
14
|
+
begin
|
15
|
+
fids = list_fids(low + 1)
|
20
16
|
fids.each { |fid| yield fid }
|
21
|
-
|
22
|
-
end
|
17
|
+
rv += fids.size
|
18
|
+
end while last = fids[-1] and low = last["fid"]
|
19
|
+
rv
|
23
20
|
end
|
24
21
|
|
25
22
|
##
|
@@ -31,18 +28,18 @@ class MogileFS::Admin < MogileFS::Client
|
|
31
28
|
# Returns:
|
32
29
|
#
|
33
30
|
# [{"status"=>"alive",
|
34
|
-
# "http_get_port"=>
|
31
|
+
# "http_get_port"=>nil,
|
35
32
|
# "http_port"=>"",
|
36
33
|
# "hostid"=>"1",
|
37
34
|
# "hostip"=>"",
|
38
35
|
# "hostname"=>"rur-1",
|
39
|
-
# "remoteroot"=>"/mnt/mogilefs/rur-1",
|
40
36
|
# "altip"=>"",
|
41
37
|
# "altmask"=>""}]
|
42
38
|
|
43
39
|
def get_hosts(hostid = nil)
|
40
|
+
to_i = { "hostid" => true, "http_port" => true, "http_get_port" => true }
|
44
41
|
clean('hosts', 'host',
|
45
|
-
@backend.get_hosts(hostid ? { :hostid => hostid } : {}))
|
42
|
+
@backend.get_hosts(hostid ? { :hostid => hostid } : {}), true, to_i)
|
46
43
|
end
|
47
44
|
|
48
45
|
##
|
@@ -54,46 +51,60 @@ class MogileFS::Admin < MogileFS::Client
|
|
54
51
|
# Returns:
|
55
52
|
#
|
56
53
|
# [{"status"=>"alive",
|
57
|
-
# "mb_asof"=>
|
58
|
-
# "mb_free"=>
|
59
|
-
# "devid"=>
|
60
|
-
# "hostid"=>
|
61
|
-
# "mb_used"=>
|
62
|
-
# "mb_total"=>
|
54
|
+
# "mb_asof"=>nil,
|
55
|
+
# "mb_free"=>666000,
|
56
|
+
# "devid"=>1,
|
57
|
+
# "hostid"=>1,
|
58
|
+
# "mb_used"=>666,
|
59
|
+
# "mb_total"=>666666}]
|
63
60
|
|
64
61
|
def get_devices(devid = nil)
|
65
|
-
|
66
|
-
|
62
|
+
to_i = {
|
63
|
+
"mb_asof" => true, "mb_free" => true,
|
64
|
+
"mb_used" => true, "mb_total" => true ,
|
65
|
+
"devid" => true, "weight" => true, "hostid" => true
|
66
|
+
}
|
67
|
+
rv = @backend.get_devices(devid ? { :devid => devid } : {})
|
68
|
+
rv = clean('devices', 'dev', rv, true, to_i)
|
69
|
+
rv.each do |row|
|
70
|
+
u = row["utilization"] and
|
71
|
+
row["utilization"] = nil == u ? nil : u.to_f
|
72
|
+
end
|
67
73
|
end
|
68
74
|
|
69
75
|
##
|
70
|
-
# Returns an Array of fid Hashes from +from_fid
|
76
|
+
# Returns an Array of fid Hashes from +from_fid+, limited to +count+
|
71
77
|
#
|
72
78
|
# admin.list_fids 0, 100
|
73
79
|
#
|
74
80
|
# Returns:
|
75
81
|
#
|
76
|
-
# [{"fid"=>
|
82
|
+
# [{"fid"=>99,
|
77
83
|
# "class"=>"normal",
|
78
84
|
# "domain"=>"test",
|
79
|
-
# "devcount"=>
|
80
|
-
# "length"=>
|
85
|
+
# "devcount"=>2,
|
86
|
+
# "length"=>4,
|
81
87
|
# "key"=>"file_key"},
|
82
|
-
# {"fid"=>
|
88
|
+
# {"fid"=>82,
|
83
89
|
# "class"=>"normal",
|
84
|
-
# "devcount"=>
|
90
|
+
# "devcount"=>2,
|
85
91
|
# "domain"=>"test",
|
86
|
-
# "length"=>
|
92
|
+
# "length"=>9,
|
87
93
|
# "key"=>"new_new_key"}]
|
88
94
|
|
89
|
-
def list_fids(from_fid,
|
95
|
+
def list_fids(from_fid, count = 100)
|
96
|
+
to_i = { "fid" => true, "devcount" => true, "length" => true }
|
97
|
+
# :to is now :count internally in mogilefsd
|
90
98
|
clean('fid_count', 'fid_',
|
91
|
-
@backend.list_fids(:from => from_fid, :to =>
|
99
|
+
@backend.list_fids(:from => from_fid, :to => count), true, to_i)
|
92
100
|
end
|
93
101
|
|
94
102
|
##
|
95
103
|
# Returns a statistics structure representing the state of mogilefs.
|
96
104
|
#
|
105
|
+
# *** This command no longer works with recent versions of MogileFS ***
|
106
|
+
# *** Use mogstats(1) from the MogileFS::Utils package on CPAN ***
|
107
|
+
#
|
97
108
|
# admin.get_stats
|
98
109
|
#
|
99
110
|
# Returns:
|
@@ -129,22 +140,47 @@ class MogileFS::Admin < MogileFS::Client
|
|
129
140
|
end
|
130
141
|
|
131
142
|
##
|
132
|
-
# Returns the domains present in the mogilefs.
|
143
|
+
# Returns the domains and classes, and their policies present in the mogilefs.
|
133
144
|
#
|
134
145
|
# admin.get_domains
|
135
146
|
#
|
136
|
-
# Returns:
|
147
|
+
# Returns (on newer MogileFS servers):
|
148
|
+
# {
|
149
|
+
# "test" => {
|
150
|
+
# "default" => {
|
151
|
+
# "mindevcount" => 2,
|
152
|
+
# "replpolicy" => "MultipleHosts()"
|
153
|
+
# }
|
154
|
+
# }
|
155
|
+
# }
|
156
|
+
#
|
157
|
+
# Returns (on older MogileFS servers without replication policies):
|
137
158
|
#
|
138
159
|
# {"test"=>{"normal"=>3, "default"=>2}}
|
139
160
|
|
140
161
|
def get_domains
|
141
162
|
res = @backend.get_domains
|
163
|
+
have_replpolicy = false
|
142
164
|
|
143
165
|
domains = {}
|
166
|
+
to_i = { "mindevcount" => true }
|
144
167
|
(1..res['domains'].to_i).each do |i|
|
145
|
-
domain = clean "domain#{i}classes", "domain#{i}class", res, false
|
146
|
-
|
147
|
-
domains[res["domain#{i}"]] =
|
168
|
+
domain = clean "domain#{i}classes", "domain#{i}class", res, false, to_i
|
169
|
+
|
170
|
+
tmp = domains[res["domain#{i}"].freeze] = {}
|
171
|
+
domain.each do |d|
|
172
|
+
tmp[d.delete("name").freeze] = d
|
173
|
+
have_replpolicy ||= d.include?("replpolicy")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# only for MogileFS 1.x?, maybe we can drop support for this...
|
178
|
+
unless have_replpolicy
|
179
|
+
domains.each do |namespace, class_data|
|
180
|
+
class_data.each do |class_name, data|
|
181
|
+
class_data[class_name] = data["mindevcount"]
|
182
|
+
end
|
183
|
+
end
|
148
184
|
end
|
149
185
|
|
150
186
|
domains
|
@@ -160,7 +196,8 @@ class MogileFS::Admin < MogileFS::Client
|
|
160
196
|
end
|
161
197
|
|
162
198
|
##
|
163
|
-
# Deletes +domain+. Returns true if successful,
|
199
|
+
# Deletes +domain+. Returns true if successful, raises
|
200
|
+
# MogileFS::Backend::DomainNotFoundError if not
|
164
201
|
|
165
202
|
def delete_domain(domain)
|
166
203
|
raise MogileFS::ReadOnlyError if readonly?
|
@@ -168,24 +205,24 @@ class MogileFS::Admin < MogileFS::Client
|
|
168
205
|
end
|
169
206
|
|
170
207
|
##
|
171
|
-
# Creates a new class in +domain+ named +klass+ with
|
172
|
-
#
|
208
|
+
# Creates a new class in +domain+ named +klass+ with +policy+ for
|
209
|
+
# replication. Raises on failure.
|
173
210
|
|
174
|
-
def create_class(domain, klass,
|
175
|
-
modify_class(domain, klass,
|
211
|
+
def create_class(domain, klass, policy)
|
212
|
+
modify_class(domain, klass, policy, :create)
|
176
213
|
end
|
177
214
|
|
178
215
|
##
|
179
|
-
# Updates class +klass+ in +domain+
|
180
|
-
#
|
216
|
+
# Updates class +klass+ in +domain+ with +policy+ for replication.
|
217
|
+
# Raises on failure.
|
181
218
|
|
182
|
-
def update_class(domain, klass,
|
183
|
-
modify_class(domain, klass,
|
219
|
+
def update_class(domain, klass, policy)
|
220
|
+
modify_class(domain, klass, policy, :update)
|
184
221
|
end
|
185
222
|
|
186
223
|
##
|
187
|
-
# Removes class +klass+ from +domain+. Returns true if successful
|
188
|
-
#
|
224
|
+
# Removes class +klass+ from +domain+. Returns true if successful.
|
225
|
+
# Raises on failure
|
189
226
|
|
190
227
|
def delete_class(domain, klass)
|
191
228
|
! @backend.delete_class(:domain => domain, :class => klass).nil?
|
@@ -226,18 +263,41 @@ class MogileFS::Admin < MogileFS::Client
|
|
226
263
|
! @backend.set_state(:host => host, :device => device, :state => state).nil?
|
227
264
|
end
|
228
265
|
|
266
|
+
# reschedules all deferred replication, returns a hash with the number
|
267
|
+
# of files rescheduled:
|
268
|
+
#
|
269
|
+
# admin.replicate_now => { "count" => 5 }
|
270
|
+
def replicate_now
|
271
|
+
rv = @backend.replicate_now
|
272
|
+
rv["count"] = rv["count"].to_i
|
273
|
+
rv
|
274
|
+
end
|
275
|
+
|
276
|
+
def clear_cache
|
277
|
+
@backend.clear_cache
|
278
|
+
end
|
279
|
+
|
229
280
|
protected unless defined? $TESTING
|
230
281
|
|
231
282
|
##
|
232
283
|
# Modifies +klass+ on +domain+ to store files on +mindevcount+ devices via
|
233
|
-
# +action+. Returns the class name if successful,
|
284
|
+
# +action+. Returns the class name if successful, raises if not
|
234
285
|
|
235
|
-
def modify_class(domain, klass,
|
286
|
+
def modify_class(domain, klass, policy, action)
|
236
287
|
raise MogileFS::ReadOnlyError if readonly?
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
288
|
+
args = { :domain => domain, :class => klass }
|
289
|
+
case policy
|
290
|
+
when Integer
|
291
|
+
args[:mindevcount] = policy
|
292
|
+
when String
|
293
|
+
args[:replpolicy] = policy
|
294
|
+
when Hash
|
295
|
+
args.merge!(policy)
|
296
|
+
else
|
297
|
+
raise ArgumentError,
|
298
|
+
"policy=#{policy.inspect} not understood for #{action}_class"
|
299
|
+
end
|
300
|
+
@backend.__send__("#{action}_class", args)["class"]
|
241
301
|
end
|
242
302
|
|
243
303
|
##
|
@@ -246,7 +306,7 @@ class MogileFS::Admin < MogileFS::Client
|
|
246
306
|
|
247
307
|
def modify_host(host, args = {}, action = 'create')
|
248
308
|
args[:host] = host
|
249
|
-
! @backend.
|
309
|
+
! @backend.__send__("#{action}_host", args).nil?
|
250
310
|
end
|
251
311
|
|
252
312
|
##
|
@@ -278,13 +338,19 @@ class MogileFS::Admin < MogileFS::Client
|
|
278
338
|
# "altip"=>"",
|
279
339
|
# "altmask"=>""}]
|
280
340
|
|
281
|
-
def clean(count, prefix, res, underscore = true)
|
282
|
-
|
341
|
+
def clean(count, prefix, res, underscore = true, to_i = [])
|
342
|
+
empty = ""
|
343
|
+
underscore = underscore ? '_' : empty
|
344
|
+
keys = res.keys
|
283
345
|
(1..res[count].to_i).map do |i|
|
284
|
-
|
285
|
-
|
346
|
+
re = /^#{prefix}#{i}#{underscore}/
|
347
|
+
row = {}
|
348
|
+
keys.grep(re).each do |k|
|
349
|
+
v = res[k]
|
350
|
+
k = k.sub(re, empty).freeze
|
351
|
+
row[k] = to_i.include?(k) ? (empty == v ? nil : v.to_i) : v
|
286
352
|
end
|
287
|
-
|
353
|
+
row
|
288
354
|
end
|
289
355
|
end
|
290
356
|
|