cleanup unused variable warnings
[ruby-mogilefs-client.git] / lib / mogilefs / admin.rb
blob0833bc2dc3c998930175bb8eac147ea9b45aa8d2
1 # -*- encoding: binary -*-
2 require 'mogilefs/client'
4 ##
5 # A MogileFS Administration Client
7 class MogileFS::Admin < MogileFS::Client
9   ##
10   # Enumerates fids using #list_fids.
12   def each_fid
13     low = 0
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 }
20       low = high + 1
21     end
22   end
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"=>""}]
42   def get_hosts(hostid = nil)
43     clean('hosts', 'host',
44           @backend.get_hosts(hostid ? { :hostid => hostid } : {}))
45   end
47   ##
48   # Returns an Array of device status Hashes.  If devid is given only that
49   # device is returned.
50   #
51   #   admin.get_devices 1
52   #
53   # Returns:
54   #
55   #   [{"status"=>"alive",
56   #     "mb_asof"=>"",
57   #     "mb_free"=>"0",
58   #     "devid"=>"1",
59   #     "hostid"=>"1",
60   #     "mb_used"=>"",
61   #     "mb_total"=>""}]
63   def get_devices(devid = nil)
64     clean('devices', 'dev',
65           @backend.get_devices(devid ? { :devid => devid } : {}))
66   end
68   ##
69   # Returns an Array of fid Hashes from +from_fid+ to +to_fid+.
70   #
71   #   admin.list_fids 0, 100
72   #
73   # Returns:
74   #
75   #   [{"fid"=>"99",
76   #     "class"=>"normal",
77   #     "domain"=>"test",
78   #     "devcount"=>"2",
79   #     "length"=>"4",
80   #     "key"=>"file_key"},
81   #    {"fid"=>"82",
82   #     "class"=>"normal",
83   #     "devcount"=>"2",
84   #     "domain"=>"test",
85   #     "length"=>"9",
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))
91   end
93   ##
94   # Returns a statistics structure representing the state of mogilefs.
95   #
96   #   admin.get_stats
97   #
98   # Returns:
99   #
100   #   {"fids"=>{"max"=>"99", "count"=>"2"},
101   #    "device"=>
102   #     [{"status"=>"alive", "files"=>"2", "id"=>"1", "host"=>"rur-1"},
103   #      {"status"=>"alive", "files"=>"2", "id"=>"2", "host"=>"rur-2"}],
104   #    "replication"=>
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
110     stats = {}
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
117       stats['fids'] = {
118         'max' => res['fidmax'].to_i,
119         'count' => res['fidcount'].to_i
120       }
121     end
123     stats.delete 'device' if stats['device'].empty?
124     stats.delete 'file' if stats['file'].empty?
125     stats.delete 'replication' if stats['replication'].empty?
127     stats
128   end
130   ##
131   # Returns the domains and classes, and their policies present in the mogilefs.
132   #
133   #   admin.get_domains
134   #
135   # Returns (on newer MogileFS servers):
136   #   {
137   #     "test" => {
138   #       "default" => {
139   #         "mindevcount" => 2,
140   #         "replpolicy" => "MultipleHosts()"
141   #       }
142   #     }
143   #   }
144   #
145   # Returns (on older MogileFS servers without replication policies):
146   #
147   #   {"test"=>{"normal"=>3, "default"=>2}}
149   def get_domains
150     res = @backend.get_domains
151     have_replpolicy = false
153     domains = {}
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}"]] = {}
158       domain.each do |d|
159         tmp[d.delete("name")] = d
160         d["mindevcount"] = d["mindevcount"].to_i
161         have_replpolicy ||= d.include?("replpolicy")
162       end
163     end
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"]
170         end
171       end
172     end
174     domains
175   end
177   ##
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
184   end
186   ##
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?
193   end
195   ##
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)
201   end
203   ##
204   # Updates class +klass+ in +domain+ with +policy+ for replication.
205   # Raises on failure.
207   def update_class(domain, klass, policy)
208     modify_class(domain, klass, policy, :update)
209   end
211   ##
212   # Removes class +klass+ from +domain+.  Returns true if successful.
213   # Raises on failure
215   def delete_class(domain, klass)
216     ! @backend.delete_class(:domain => domain, :class => klass).nil?
217   end
219   ##
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')
228   end
230   ##
231   # Updates +host+ with +args+.  Returns true if successful, false if not.
233   def update_host(host, args = {})
234     modify_host(host, args, 'update')
235   end
237   ##
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?
243   end
245   ##
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?
252   end
254   protected unless defined? $TESTING
256   ##
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 }
263     case policy
264     when Integer
265       args[:mindevcount] = policy
266     when String
267       args[:replpolicy] = policy
268     when Hash
269       args.merge!(policy)
270     else
271       raise ArgumentError,
272            "policy=#{policy.inspect} not understood for #{action}_class"
273     end
274     @backend.__send__("#{action}_class", args)["class"]
275   end
277   ##
278   # Modifies +host+ using +args+ via +action+.  Returns true if successful,
279   # false if not.
281   def modify_host(host, args = {}, action = 'create')
282     args[:host] = host
283     ! @backend.send("#{action}_host", args).nil?
284   end
286   ##
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.
290   #
291   #   res = {"host1_remoteroot"=>"/mnt/mogilefs/rur-1",
292   #          "host1_hostname"=>"rur-1",
293   #          "host1_hostid"=>"1",
294   #          "host1_http_get_port"=>"",
295   #          "host1_altip"=>"",
296   #          "hosts"=>"1",
297   #          "host1_hostip"=>"",
298   #          "host1_http_port"=>"",
299   #          "host1_status"=>"alive",
300   #          "host1_altmask"=>""}
301   #   admin.clean 'hosts', 'host', res
302   #
303   # Returns:
304   #
305   #   [{"status"=>"alive",
306   #     "http_get_port"=>"",
307   #     "http_port"=>"",
308   #     "hostid"=>"1",
309   #     "hostip"=>"",
310   #     "hostname"=>"rur-1",
311   #     "remoteroot"=>"/mnt/mogilefs/rur-1",
312   #     "altip"=>"",
313   #     "altmask"=>""}]
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]
320       end
321       Hash[*dev.flatten]
322     end
323   end