1 # -*- encoding: binary -*-
2 require 'mogilefs/client'
5 # A MogileFS Administration Client
7 class MogileFS::Admin < MogileFS::Client
10 # Enumerates fids using #list_fids.
16 max = get_stats('fids')['fids']['max']
18 0.step max, 100 do |high|
19 fids = list_fids low, high
20 fids.each { |fid| yield fid }
26 # Returns an Array of host status Hashes. If +hostid+ is given only that
33 # [{"status"=>"alive",
34 # "http_get_port"=>"",
38 # "hostname"=>"rur-1",
39 # "remoteroot"=>"/mnt/mogilefs/rur-1",
43 def get_hosts(hostid = nil)
44 clean('hosts', 'host',
45 @backend.get_hosts(hostid ? { :hostid => hostid } : {}))
49 # Returns an Array of device status Hashes. If devid is given only that
56 # [{"status"=>"alive",
64 def get_devices(devid = nil)
65 clean('devices', 'dev',
66 @backend.get_devices(devid ? { :devid => devid } : {}))
70 # Returns an Array of fid Hashes from +from_fid+ to +to_fid+.
72 # admin.list_fids 0, 100
87 # "key"=>"new_new_key"}]
89 def list_fids(from_fid, to_fid)
90 clean('fid_count', 'fid_',
91 @backend.list_fids(:from => from_fid, :to => to_fid))
95 # Returns a statistics structure representing the state of mogilefs.
101 # {"fids"=>{"max"=>"99", "count"=>"2"},
103 # [{"status"=>"alive", "files"=>"2", "id"=>"1", "host"=>"rur-1"},
104 # {"status"=>"alive", "files"=>"2", "id"=>"2", "host"=>"rur-2"}],
106 # [{"files"=>"2", "class"=>"normal", "devcount"=>"2", "domain"=>"test"}],
107 # "file"=>[{"files"=>"2", "class"=>"normal", "domain"=>"test"}]}
109 def get_stats(type = 'all')
110 res = @backend.stats type => 1
113 stats['device'] = clean 'devicescount', 'devices', res, false
114 stats['file'] = clean 'filescount', 'files', res, false
115 stats['replication'] = clean 'replicationcount', 'replication', res, false
117 if res['fidmax'] or res['fidcount'] then
119 'max' => res['fidmax'].to_i,
120 'count' => res['fidcount'].to_i
124 stats.delete 'device' if stats['device'].empty?
125 stats.delete 'file' if stats['file'].empty?
126 stats.delete 'replication' if stats['replication'].empty?
132 # Returns the domains and classes, and their policies present in the mogilefs.
136 # Returns (on newer MogileFS servers):
140 # "mindevcount" => 2,
141 # "replpolicy" => "MultipleHosts()"
146 # Returns (on older MogileFS servers without replication policies):
148 # {"test"=>{"normal"=>3, "default"=>2}}
151 res = @backend.get_domains
152 have_replpolicy = false
155 (1..res['domains'].to_i).each do |i|
156 domain = clean "domain#{i}classes", "domain#{i}class", res, false
158 tmp = domains[res["domain#{i}"]] = {}
160 tmp[d.delete("name")] = d
161 d["mindevcount"] = d["mindevcount"].to_i
162 have_replpolicy ||= d.include?("replpolicy")
166 # only for MogileFS 1.x?, maybe we can drop support for this...
167 unless have_replpolicy
168 domains.each do |namespace, class_data|
169 class_data.each do |class_name, data|
170 class_data[class_name] = data["mindevcount"]
179 # Creates a new domain named +domain+. Returns nil if creation failed.
181 def create_domain(domain)
182 raise MogileFS::ReadOnlyError if readonly?
183 res = @backend.create_domain :domain => domain
184 res ? res['domain'] : nil
188 # Deletes +domain+. Returns true if successful, raises
189 # MogileFS::Backend::DomainNotFoundError if not
191 def delete_domain(domain)
192 raise MogileFS::ReadOnlyError if readonly?
193 ! @backend.delete_domain(:domain => domain).nil?
197 # Creates a new class in +domain+ named +klass+ with files replicated to
198 # +mindevcount+ devices. Returns nil on failure.
200 def create_class(domain, klass, mindevcount)
201 modify_class(domain, klass, mindevcount, :create)
205 # Updates class +klass+ in +domain+ to be replicated to +mindevcount+
206 # devices. Returns nil on failure.
208 def update_class(domain, klass, mindevcount)
209 modify_class(domain, klass, mindevcount, :update)
213 # Removes class +klass+ from +domain+. Returns true if successful, false if
216 def delete_class(domain, klass)
217 ! @backend.delete_class(:domain => domain, :class => klass).nil?
221 # Creates a new host named +host+. +args+ must contain :ip and :port.
222 # Returns true if successful, false if not.
224 def create_host(host, args = {})
225 raise ArgumentError, "Must specify ip and port" unless \
226 args.include? :ip and args.include? :port
228 modify_host(host, args, 'create')
232 # Updates +host+ with +args+. Returns true if successful, false if not.
234 def update_host(host, args = {})
235 modify_host(host, args, 'update')
239 # Deletes host +host+. Returns nil on failure.
241 def delete_host(host)
242 raise MogileFS::ReadOnlyError if readonly?
243 ! @backend.delete_host(:host => host).nil?
247 # Changes the device status of +device+ on +host+ to +state+ which can be
248 # 'alive', 'down', or 'dead'.
250 def change_device_state(host, device, state)
251 raise MogileFS::ReadOnlyError if readonly?
252 ! @backend.set_state(:host => host, :device => device, :state => state).nil?
255 protected unless defined? $TESTING
258 # Modifies +klass+ on +domain+ to store files on +mindevcount+ devices via
259 # +action+. Returns the class name if successful, nil if not.
261 def modify_class(domain, klass, mindevcount, action)
262 raise MogileFS::ReadOnlyError if readonly?
263 res = @backend.send("#{action}_class", :domain => domain, :class => klass,
264 :mindevcount => mindevcount)
266 res ? res['class'] : nil
270 # Modifies +host+ using +args+ via +action+. Returns true if successful,
273 def modify_host(host, args = {}, action = 'create')
275 ! @backend.send("#{action}_host", args).nil?
279 # Turns the response +res+ from the backend into an Array of Hashes from 1
280 # to res[+count+]. If +underscore+ is true then a '_' character is assumed
281 # between the prefix and the hash key value.
283 # res = {"host1_remoteroot"=>"/mnt/mogilefs/rur-1",
284 # "host1_hostname"=>"rur-1",
285 # "host1_hostid"=>"1",
286 # "host1_http_get_port"=>"",
289 # "host1_hostip"=>"",
290 # "host1_http_port"=>"",
291 # "host1_status"=>"alive",
292 # "host1_altmask"=>""}
293 # admin.clean 'hosts', 'host', res
297 # [{"status"=>"alive",
298 # "http_get_port"=>"",
302 # "hostname"=>"rur-1",
303 # "remoteroot"=>"/mnt/mogilefs/rur-1",
307 def clean(count, prefix, res, underscore = true)
308 underscore = underscore ? '_' : ''
309 (1..res[count].to_i).map do |i|
310 dev = res.select { |k,_| k =~ /^#{prefix}#{i}#{underscore}/ }.map do |k,v|
311 [k.sub(/^#{prefix}#{i}#{underscore}/, ''), v]