pager-mogilefs-client 1.2.1.20080519
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +11 -0
- data/LICENSE.txt +27 -0
- data/Manifest.txt +19 -0
- data/README.txt +66 -0
- data/Rakefile +18 -0
- data/lib/mogilefs.rb +26 -0
- data/lib/mogilefs/admin.rb +298 -0
- data/lib/mogilefs/backend.rb +222 -0
- data/lib/mogilefs/client.rb +65 -0
- data/lib/mogilefs/httpfile.rb +118 -0
- data/lib/mogilefs/mogilefs.rb +237 -0
- data/lib/mogilefs/nfsfile.rb +81 -0
- data/lib/mogilefs/pool.rb +50 -0
- data/test/setup.rb +54 -0
- data/test/test_admin.rb +174 -0
- data/test/test_backend.rb +220 -0
- data/test/test_client.rb +53 -0
- data/test/test_mogilefs.rb +160 -0
- data/test/test_pool.rb +98 -0
- metadata +96 -0
data/History.txt
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright 2005 Eric Hodel, The Robot Co-op. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions
|
5
|
+
are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
3. Neither the names of the authors nor the names of their contributors
|
13
|
+
may be used to endorse or promote products derived from this software
|
14
|
+
without specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
|
17
|
+
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
19
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
|
20
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
21
|
+
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
22
|
+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
23
|
+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
24
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
25
|
+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
26
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
lib/mogilefs.rb
|
7
|
+
lib/mogilefs/admin.rb
|
8
|
+
lib/mogilefs/backend.rb
|
9
|
+
lib/mogilefs/client.rb
|
10
|
+
lib/mogilefs/httpfile.rb
|
11
|
+
lib/mogilefs/mogilefs.rb
|
12
|
+
lib/mogilefs/nfsfile.rb
|
13
|
+
lib/mogilefs/pool.rb
|
14
|
+
test/setup.rb
|
15
|
+
test/test_admin.rb
|
16
|
+
test/test_backend.rb
|
17
|
+
test/test_client.rb
|
18
|
+
test/test_mogilefs.rb
|
19
|
+
test/test_pool.rb
|
data/README.txt
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
= mogilefs-client
|
2
|
+
|
3
|
+
A Ruby MogileFS client
|
4
|
+
|
5
|
+
Rubyforge Project:
|
6
|
+
|
7
|
+
http://rubyforge.org/projects/seattlerb/
|
8
|
+
|
9
|
+
Documentation:
|
10
|
+
|
11
|
+
http://seattlerb.org/mogilefs-client
|
12
|
+
|
13
|
+
File bugs:
|
14
|
+
|
15
|
+
http://rubyforge.org/tracker/?func=add&group_id=1513&atid=5921
|
16
|
+
|
17
|
+
== About
|
18
|
+
|
19
|
+
A Ruby MogileFS client. MogileFS is a distributed filesystem written
|
20
|
+
by Danga Interactive. This client supports NFS and HTTP modes.
|
21
|
+
|
22
|
+
For information on MogileFS see:
|
23
|
+
|
24
|
+
http://danga.com/mogilefs/
|
25
|
+
|
26
|
+
== Installing mogilefs-client
|
27
|
+
|
28
|
+
First you need a MogileFS setup. You can find information on how to do that at the above URL.
|
29
|
+
|
30
|
+
Then install the gem:
|
31
|
+
|
32
|
+
$ sudo gem install mogilefs-client
|
33
|
+
|
34
|
+
== Using mogilefs-client
|
35
|
+
|
36
|
+
# Create a new instance that will communicate with these trackers:
|
37
|
+
hosts = %w[192.168.1.69:6001 192.168.1.70:6001]
|
38
|
+
mg = MogileFS::MogileFS.new(:domain => 'test', :hosts => hosts
|
39
|
+
:root => '/mnt/mogilefs')
|
40
|
+
|
41
|
+
# Stores "A bunch of text to store" into 'some_key' with a class of 'text'.
|
42
|
+
mg.store_content 'some_key', 'text', "A bunch of text to store"
|
43
|
+
|
44
|
+
# Retrieve data from 'some_key'
|
45
|
+
data = mg.get_file_data 'some_key'
|
46
|
+
|
47
|
+
# Store the contents of 'image.jpeg' into the key 'my_image' with a class of
|
48
|
+
# 'image'.
|
49
|
+
mg.store_file 'my_image', 'image', 'image.jpeg'
|
50
|
+
|
51
|
+
# Store the contents of 'image.jpeg' into the key 'my_image' with a class of
|
52
|
+
# 'image' using an open IO.
|
53
|
+
File.open 'image.jpeg' do |fp|
|
54
|
+
mg.store_file 'my_image', 'image', fp
|
55
|
+
end
|
56
|
+
|
57
|
+
# Remove the key 'my_image' and 'some_key'.
|
58
|
+
mg.delete 'my_image'
|
59
|
+
mg.delete 'some_key'
|
60
|
+
|
61
|
+
== WARNING!
|
62
|
+
|
63
|
+
This client is only known to work in NFS mode. HTTP mode is implemented but
|
64
|
+
only lightly tested in production environments. If you find a bug,
|
65
|
+
please report it on the Rubyforge project.
|
66
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
|
4
|
+
$:.unshift 'lib'
|
5
|
+
require 'mogilefs'
|
6
|
+
|
7
|
+
Hoe.new 'mogilefs-client', MogileFS::VERSION do |p|
|
8
|
+
p.rubyforge_name = 'seattlerb'
|
9
|
+
p.author = 'Eric Hodel'
|
10
|
+
p.email = 'drbrain@segment7.net'
|
11
|
+
p.summary = p.paragraphs_of('README.txt', 1).first
|
12
|
+
p.description = p.paragraphs_of('README.txt', 9).first
|
13
|
+
p.url = p.paragraphs_of('README.txt', 5).first
|
14
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
15
|
+
end
|
16
|
+
|
17
|
+
# vim: syntax=Ruby
|
18
|
+
|
data/lib/mogilefs.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
##
|
2
|
+
# MogileFS is a Ruby client for Danga Interactive's open source distributed
|
3
|
+
# filesystem.
|
4
|
+
#
|
5
|
+
# To read more about Danga's MogileFS: http://danga.com/mogilefs/
|
6
|
+
|
7
|
+
module MogileFS
|
8
|
+
|
9
|
+
VERSION = '1.2.1'
|
10
|
+
|
11
|
+
##
|
12
|
+
# Raised when a socket remains unreadable for too long.
|
13
|
+
|
14
|
+
class UnreadableSocketError < RuntimeError; end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'socket'
|
19
|
+
|
20
|
+
require 'mogilefs/backend'
|
21
|
+
require 'mogilefs/nfsfile'
|
22
|
+
require 'mogilefs/httpfile'
|
23
|
+
require 'mogilefs/client'
|
24
|
+
require 'mogilefs/mogilefs'
|
25
|
+
require 'mogilefs/admin'
|
26
|
+
|
@@ -0,0 +1,298 @@
|
|
1
|
+
require 'mogilefs/client'
|
2
|
+
|
3
|
+
##
|
4
|
+
# A MogileFS Administration Client
|
5
|
+
|
6
|
+
class MogileFS::Admin < MogileFS::Client
|
7
|
+
|
8
|
+
##
|
9
|
+
# Enumerates fids using #list_fids.
|
10
|
+
|
11
|
+
def each_fid
|
12
|
+
low = 0
|
13
|
+
high = nil
|
14
|
+
|
15
|
+
max = get_stats('fids')['fids']['max']
|
16
|
+
|
17
|
+
0.step max, 100 do |high|
|
18
|
+
fids = list_fids low, high
|
19
|
+
fids.each { |fid| yield fid }
|
20
|
+
low = high + 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Returns an Array of host status Hashes. If +hostid+ is given only that
|
26
|
+
# host is returned.
|
27
|
+
#
|
28
|
+
# admin.get_hosts 1
|
29
|
+
#
|
30
|
+
# Returns:
|
31
|
+
#
|
32
|
+
# [{"status"=>"alive",
|
33
|
+
# "http_get_port"=>"",
|
34
|
+
# "http_port"=>"",
|
35
|
+
# "hostid"=>"1",
|
36
|
+
# "hostip"=>"",
|
37
|
+
# "hostname"=>"rur-1",
|
38
|
+
# "remoteroot"=>"/mnt/mogilefs/rur-1",
|
39
|
+
# "altip"=>"",
|
40
|
+
# "altmask"=>""}]
|
41
|
+
|
42
|
+
def get_hosts(hostid = nil)
|
43
|
+
args = hostid ? { :hostid => hostid } : {}
|
44
|
+
res = @backend.get_hosts args
|
45
|
+
return clean('hosts', 'host', res)
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Returns an Array of device status Hashes. If devid is given only that
|
50
|
+
# device is returned.
|
51
|
+
#
|
52
|
+
# admin.get_devices 1
|
53
|
+
#
|
54
|
+
# Returns:
|
55
|
+
#
|
56
|
+
# [{"status"=>"alive",
|
57
|
+
# "mb_asof"=>"",
|
58
|
+
# "mb_free"=>"0",
|
59
|
+
# "devid"=>"1",
|
60
|
+
# "hostid"=>"1",
|
61
|
+
# "mb_used"=>"",
|
62
|
+
# "mb_total"=>""}]
|
63
|
+
|
64
|
+
def get_devices(devid = nil)
|
65
|
+
args = devid ? { :devid => devid } : {}
|
66
|
+
res = @backend.get_devices args
|
67
|
+
return clean('devices', 'dev', res)
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Returns an Array of fid Hashes from +from_fid+ to +to_fid+.
|
72
|
+
#
|
73
|
+
# admin.list_fids 0, 100
|
74
|
+
#
|
75
|
+
# Returns:
|
76
|
+
#
|
77
|
+
# [{"fid"=>"99",
|
78
|
+
# "class"=>"normal",
|
79
|
+
# "domain"=>"test",
|
80
|
+
# "devcount"=>"2",
|
81
|
+
# "length"=>"4",
|
82
|
+
# "key"=>"file_key"},
|
83
|
+
# {"fid"=>"82",
|
84
|
+
# "class"=>"normal",
|
85
|
+
# "devcount"=>"2",
|
86
|
+
# "domain"=>"test",
|
87
|
+
# "length"=>"9",
|
88
|
+
# "key"=>"new_new_key"}]
|
89
|
+
|
90
|
+
def list_fids(from_fid, to_fid)
|
91
|
+
res = @backend.list_fids :from => from_fid, :to => to_fid
|
92
|
+
return clean('fid_count', 'fid_', res)
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Returns a statistics structure representing the state of mogilefs.
|
97
|
+
#
|
98
|
+
# admin.get_stats
|
99
|
+
#
|
100
|
+
# Returns:
|
101
|
+
#
|
102
|
+
# {"fids"=>{"max"=>"99", "count"=>"2"},
|
103
|
+
# "device"=>
|
104
|
+
# [{"status"=>"alive", "files"=>"2", "id"=>"1", "host"=>"rur-1"},
|
105
|
+
# {"status"=>"alive", "files"=>"2", "id"=>"2", "host"=>"rur-2"}],
|
106
|
+
# "replication"=>
|
107
|
+
# [{"files"=>"2", "class"=>"normal", "devcount"=>"2", "domain"=>"test"}],
|
108
|
+
# "file"=>[{"files"=>"2", "class"=>"normal", "domain"=>"test"}]}
|
109
|
+
|
110
|
+
def get_stats(type = 'all')
|
111
|
+
res = @backend.stats type => 1
|
112
|
+
stats = {}
|
113
|
+
|
114
|
+
stats['device'] = clean 'devicescount', 'devices', res, false
|
115
|
+
stats['file'] = clean 'filescount', 'files', res, false
|
116
|
+
stats['replication'] = clean 'replicationcount', 'replication', res, false
|
117
|
+
|
118
|
+
if res['fidmax'] or res['fidcount'] then
|
119
|
+
stats['fids'] = {
|
120
|
+
'max' => res['fidmax'].to_i,
|
121
|
+
'count' => res['fidcount'].to_i
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
stats.delete 'device' if stats['device'].empty?
|
126
|
+
stats.delete 'file' if stats['file'].empty?
|
127
|
+
stats.delete 'replication' if stats['replication'].empty?
|
128
|
+
|
129
|
+
return stats
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Returns the domains present in the mogilefs.
|
134
|
+
#
|
135
|
+
# admin.get_domains
|
136
|
+
#
|
137
|
+
# Returns:
|
138
|
+
#
|
139
|
+
# {"test"=>{"normal"=>3, "default"=>2}}
|
140
|
+
|
141
|
+
def get_domains
|
142
|
+
res = @backend.get_domains
|
143
|
+
|
144
|
+
domains = {}
|
145
|
+
(1..res['domains'].to_i).each do |i|
|
146
|
+
domain = clean "domain#{i}classes", "domain#{i}class", res, false
|
147
|
+
domain = domain.map { |d| [d.values.first, d.values.last.to_i] }
|
148
|
+
domains[res["domain#{i}"]] = Hash[*domain.flatten]
|
149
|
+
end
|
150
|
+
|
151
|
+
return domains
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# Creates a new domain named +domain+. Returns nil if creation failed.
|
156
|
+
|
157
|
+
def create_domain(domain)
|
158
|
+
raise 'readonly mogilefs' if readonly?
|
159
|
+
res = @backend.create_domain :domain => domain
|
160
|
+
return res['domain'] unless res.nil?
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Deletes +domain+. Returns true if successful, false if not.
|
165
|
+
|
166
|
+
def delete_domain(domain)
|
167
|
+
raise 'readonly mogilefs' if readonly?
|
168
|
+
res = @backend.delete_domain :domain => domain
|
169
|
+
return !res.nil?
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Creates a new class in +domain+ named +klass+ with files replicated to
|
174
|
+
# +mindevcount+ devices. Returns nil on failure.
|
175
|
+
|
176
|
+
def create_class(domain, klass, mindevcount)
|
177
|
+
return modify_class(domain, klass, mindevcount, :create)
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Updates class +klass+ in +domain+ to be replicated to +mindevcount+
|
182
|
+
# devices. Returns nil on failure.
|
183
|
+
|
184
|
+
def update_class(domain, klass, mindevcount)
|
185
|
+
return modify_class(domain, klass, mindevcount, :update)
|
186
|
+
end
|
187
|
+
|
188
|
+
##
|
189
|
+
# Removes class +klass+ from +domain+. Returns true if successful, false if
|
190
|
+
# not.
|
191
|
+
|
192
|
+
def delete_class(domain, klass)
|
193
|
+
res = @backend.delete_class :domain => domain, :class => klass
|
194
|
+
return !res.nil?
|
195
|
+
end
|
196
|
+
|
197
|
+
##
|
198
|
+
# Creates a new host named +host+. +args+ must contain :ip and :port.
|
199
|
+
# Returns true if successful, false if not.
|
200
|
+
|
201
|
+
def create_host(host, args = {})
|
202
|
+
raise ArgumentError, "Must specify ip and port" unless \
|
203
|
+
args.include? :ip and args.include? :port
|
204
|
+
|
205
|
+
return modify_host(host, args, 'create')
|
206
|
+
end
|
207
|
+
|
208
|
+
##
|
209
|
+
# Updates +host+ with +args+. Returns true if successful, false if not.
|
210
|
+
|
211
|
+
def update_host(host, args = {})
|
212
|
+
return modify_host(host, args, 'update')
|
213
|
+
end
|
214
|
+
|
215
|
+
##
|
216
|
+
# Deletes host +host+. Returns nil on failure.
|
217
|
+
|
218
|
+
def delete_host(host)
|
219
|
+
raise 'readonly mogilefs' if readonly?
|
220
|
+
res = @backend.delete_host :host => host
|
221
|
+
return !res.nil?
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# Changes the device status of +device+ on +host+ to +state+ which can be
|
226
|
+
# 'alive', 'down', or 'dead'.
|
227
|
+
|
228
|
+
def change_device_state(host, device, state)
|
229
|
+
raise 'readonly mogilefs' if readonly?
|
230
|
+
res = @backend.set_state :host => host, :device => device, :state => state
|
231
|
+
return !res.nil?
|
232
|
+
end
|
233
|
+
|
234
|
+
protected unless defined? $TESTING
|
235
|
+
|
236
|
+
##
|
237
|
+
# Modifies +klass+ on +domain+ to store files on +mindevcount+ devices via
|
238
|
+
# +action+. Returns the class name if successful, nil if not.
|
239
|
+
|
240
|
+
def modify_class(domain, klass, mindevcount, action)
|
241
|
+
raise 'readonly mogilefs' if readonly?
|
242
|
+
res = @backend.send("#{action}_class", :domain => domain, :class => klass,
|
243
|
+
:mindevcount => mindevcount)
|
244
|
+
|
245
|
+
return res['class'] unless res.nil?
|
246
|
+
end
|
247
|
+
|
248
|
+
##
|
249
|
+
# Modifies +host+ using +args+ via +action+. Returns true if successful,
|
250
|
+
# false if not.
|
251
|
+
|
252
|
+
def modify_host(host, args = {}, action = 'create')
|
253
|
+
args[:host] = host
|
254
|
+
res = @backend.send "#{action}_host", args
|
255
|
+
return !res.nil?
|
256
|
+
end
|
257
|
+
|
258
|
+
##
|
259
|
+
# Turns the response +res+ from the backend into an Array of Hashes from 1
|
260
|
+
# to res[+count+]. If +underscore+ is true then a '_' character is assumed
|
261
|
+
# between the prefix and the hash key value.
|
262
|
+
#
|
263
|
+
# res = {"host1_remoteroot"=>"/mnt/mogilefs/rur-1",
|
264
|
+
# "host1_hostname"=>"rur-1",
|
265
|
+
# "host1_hostid"=>"1",
|
266
|
+
# "host1_http_get_port"=>"",
|
267
|
+
# "host1_altip"=>"",
|
268
|
+
# "hosts"=>"1",
|
269
|
+
# "host1_hostip"=>"",
|
270
|
+
# "host1_http_port"=>"",
|
271
|
+
# "host1_status"=>"alive",
|
272
|
+
# "host1_altmask"=>""}
|
273
|
+
# admin.clean 'hosts', 'host', res
|
274
|
+
#
|
275
|
+
# Returns:
|
276
|
+
#
|
277
|
+
# [{"status"=>"alive",
|
278
|
+
# "http_get_port"=>"",
|
279
|
+
# "http_port"=>"",
|
280
|
+
# "hostid"=>"1",
|
281
|
+
# "hostip"=>"",
|
282
|
+
# "hostname"=>"rur-1",
|
283
|
+
# "remoteroot"=>"/mnt/mogilefs/rur-1",
|
284
|
+
# "altip"=>"",
|
285
|
+
# "altmask"=>""}]
|
286
|
+
|
287
|
+
def clean(count, prefix, res, underscore = true)
|
288
|
+
underscore = underscore ? '_' : ''
|
289
|
+
return (1..res[count].to_i).map do |i|
|
290
|
+
dev = res.select { |k,_| k =~ /^#{prefix}#{i}#{underscore}/ }.map do |k,v|
|
291
|
+
[k.sub(/^#{prefix}#{i}#{underscore}/, ''), v]
|
292
|
+
end
|
293
|
+
Hash[*dev.flatten]
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|