1 # -*- encoding: binary -*-
2 require 'mogilefs/client'
5 # A MogileFS Administration Client
7 class MogileFS::Admin < MogileFS::Client
10 # Enumerates fids using #list_fids.
15 max = get_stats('fids')['fids']['max']
17 0.step max, 100 do |high|
18 fids = list_fids low, high
19 fids.each { |fid| yield fid }
25 # Returns an Array of host status Hashes. If +hostid+ is given only that
32 # [{"status"=>"alive",
33 # "http_get_port"=>"",
37 # "hostname"=>"rur-1",
38 # "remoteroot"=>"/mnt/mogilefs/rur-1",
42 def get_hosts(hostid = nil)
43 clean('hosts', 'host',
44 @backend.get_hosts(hostid ? { :hostid => hostid } : {}))
48 # Returns an Array of device status Hashes. If devid is given only that
55 # [{"status"=>"alive",
63 def get_devices(devid = nil)
64 clean('devices', 'dev',
65 @backend.get_devices(devid ? { :devid => devid } : {}))
69 # Returns an Array of fid Hashes from +from_fid+ to +to_fid+.
71 # admin.list_fids 0, 100
86 # "key"=>"new_new_key"}]
88 def list_fids(from_fid, to_fid)
89 clean('fid_count', 'fid_',
90 @backend.list_fids(:from => from_fid, :to => to_fid))
94 # Returns a statistics structure representing the state of mogilefs.
100 # {"fids"=>{"max"=>"99", "count"=>"2"},
102 # [{"status"=>"alive", "files"=>"2", "id"=>"1", "host"=>"rur-1"},
103 # {"status"=>"alive", "files"=>"2", "id"=>"2", "host"=>"rur-2"}],
105 # [{"files"=>"2", "class"=>"normal", "devcount"=>"2", "domain"=>"test"}],
106 # "file"=>[{"files"=>"2", "class"=>"normal", "domain"=>"test"}]}
108 def get_stats(type = 'all')
109 res = @backend.stats type => 1
112 stats['device'] = clean 'devicescount', 'devices', res, false
113 stats['file'] = clean 'filescount', 'files', res, false
114 stats['replication'] = clean 'replicationcount', 'replication', res, false
116 if res['fidmax'] or res['fidcount'] then
118 'max' => res['fidmax'].to_i,
119 'count' => res['fidcount'].to_i
123 stats.delete 'device' if stats['device'].empty?
124 stats.delete 'file' if stats['file'].empty?
125 stats.delete 'replication' if stats['replication'].empty?
131 # Returns the domains and classes, and their policies present in the mogilefs.
135 # Returns (on newer MogileFS servers):
139 # "mindevcount" => 2,
140 # "replpolicy" => "MultipleHosts()"
145 # Returns (on older MogileFS servers without replication policies):
147 # {"test"=>{"normal"=>3, "default"=>2}}
150 res = @backend.get_domains
151 have_replpolicy = false
154 (1..res['domains'].to_i).each do |i|
155 domain = clean "domain#{i}classes", "domain#{i}class", res, false
157 tmp = domains[res["domain#{i}"]] = {}
159 tmp[d.delete("name")] = d
160 d["mindevcount"] = d["mindevcount"].to_i
161 have_replpolicy ||= d.include?("replpolicy")
165 # only for MogileFS 1.x?, maybe we can drop support for this...
166 unless have_replpolicy
167 domains.each do |namespace, class_data|
168 class_data.each do |class_name, data|
169 class_data[class_name] = data["mindevcount"]
178 # Creates a new domain named +domain+. Returns nil if creation failed.
180 def create_domain(domain)
181 raise MogileFS::ReadOnlyError if readonly?
182 res = @backend.create_domain :domain => domain
183 res ? res['domain'] : nil
187 # Deletes +domain+. Returns true if successful, raises
188 # MogileFS::Backend::DomainNotFoundError if not
190 def delete_domain(domain)
191 raise MogileFS::ReadOnlyError if readonly?
192 ! @backend.delete_domain(:domain => domain).nil?
196 # Creates a new class in +domain+ named +klass+ with +policy+ for
197 # replication. Raises on failure.
199 def create_class(domain, klass, policy)
200 modify_class(domain, klass, policy, :create)
204 # Updates class +klass+ in +domain+ with +policy+ for replication.
207 def update_class(domain, klass, policy)
208 modify_class(domain, klass, policy, :update)
212 # Removes class +klass+ from +domain+. Returns true if successful.
215 def delete_class(domain, klass)
216 ! @backend.delete_class(:domain => domain, :class => klass).nil?
220 # Creates a new host named +host+. +args+ must contain :ip and :port.
221 # Returns true if successful, false if not.
223 def create_host(host, args = {})
224 raise ArgumentError, "Must specify ip and port" unless \
225 args.include? :ip and args.include? :port
227 modify_host(host, args, 'create')
231 # Updates +host+ with +args+. Returns true if successful, false if not.
233 def update_host(host, args = {})
234 modify_host(host, args, 'update')
238 # Deletes host +host+. Returns nil on failure.
240 def delete_host(host)
241 raise MogileFS::ReadOnlyError if readonly?
242 ! @backend.delete_host(:host => host).nil?
246 # Changes the device status of +device+ on +host+ to +state+ which can be
247 # 'alive', 'down', or 'dead'.
249 def change_device_state(host, device, state)
250 raise MogileFS::ReadOnlyError if readonly?
251 ! @backend.set_state(:host => host, :device => device, :state => state).nil?
254 protected unless defined? $TESTING
257 # Modifies +klass+ on +domain+ to store files on +mindevcount+ devices via
258 # +action+. Returns the class name if successful, raises if not
260 def modify_class(domain, klass, policy, action)
261 raise MogileFS::ReadOnlyError if readonly?
262 args = { :domain => domain, :class => klass }
265 args[:mindevcount] = policy
267 args[:replpolicy] = policy
272 "policy=#{policy.inspect} not understood for #{action}_class"
274 @backend.__send__("#{action}_class", args)["class"]
278 # Modifies +host+ using +args+ via +action+. Returns true if successful,
281 def modify_host(host, args = {}, action = 'create')
283 ! @backend.send("#{action}_host", args).nil?
287 # Turns the response +res+ from the backend into an Array of Hashes from 1
288 # to res[+count+]. If +underscore+ is true then a '_' character is assumed
289 # between the prefix and the hash key value.
291 # res = {"host1_remoteroot"=>"/mnt/mogilefs/rur-1",
292 # "host1_hostname"=>"rur-1",
293 # "host1_hostid"=>"1",
294 # "host1_http_get_port"=>"",
297 # "host1_hostip"=>"",
298 # "host1_http_port"=>"",
299 # "host1_status"=>"alive",
300 # "host1_altmask"=>""}
301 # admin.clean 'hosts', 'host', res
305 # [{"status"=>"alive",
306 # "http_get_port"=>"",
310 # "hostname"=>"rur-1",
311 # "remoteroot"=>"/mnt/mogilefs/rur-1",
315 def clean(count, prefix, res, underscore = true)
316 underscore = underscore ? '_' : ''
317 (1..res[count].to_i).map do |i|
318 dev = res.select { |k,_| k =~ /^#{prefix}#{i}#{underscore}/ }.map do |k,v|
319 [k.sub(/^#{prefix}#{i}#{underscore}/, ''), v]