mogilefs-client 2.2.0 → 3.0.0.rc1
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/.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
|
|