Branching mogilefs-client to version 1.2.1
[ruby-mogilefs-client.git] / lib / mogilefs / admin.rb
blobc03b394cabd9899273d21f0255cd410edc1ab473
1 require 'mogilefs/client'
3 ##
4 # A MogileFS Administration Client
6 class MogileFS::Admin < MogileFS::Client
8   ##
9   # Enumerates fids using #list_fids.
11   def each_fid
12     low = 0
13     high = nil
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     args = hostid ? { :hostid => hostid } : {}
44     res = @backend.get_hosts args
45     return clean('hosts', 'host', res)
46   end
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"=>""}]
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
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"}]
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
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"}]}
110   def get_stats(type = 'all')
111     res = @backend.stats type => 1
112     stats = {}
114     stats['device'] = clean 'devicescount', 'devices', res, false
115     stats['file'] = clean 'filescount', 'files', res, false
116     stats['replication'] = clean 'replicationcount', 'replication', res, false
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
125     stats.delete 'device' if stats['device'].empty?
126     stats.delete 'file' if stats['file'].empty?
127     stats.delete 'replication' if stats['replication'].empty?
129     return stats
130   end
132   ##
133   # Returns the domains present in the mogilefs.
134   #
135   #   admin.get_domains
136   #
137   # Returns:
138   #
139   #   {"test"=>{"normal"=>3, "default"=>2}}
141   def get_domains
142     res = @backend.get_domains
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
151     return domains
152   end
154   ##
155   # Creates a new domain named +domain+.  Returns nil if creation failed.
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
163   ##
164   # Deletes +domain+.  Returns true if successful, false if not.
166   def delete_domain(domain)
167     raise 'readonly mogilefs' if readonly?
168     res = @backend.delete_domain :domain => domain
169     return !res.nil?
170   end
172   ##
173   # Creates a new class in +domain+ named +klass+ with files replicated to
174   # +mindevcount+ devices.  Returns nil on failure.
176   def create_class(domain, klass, mindevcount)
177     return modify_class(domain, klass, mindevcount, :create)
178   end
180   ##
181   # Updates class +klass+ in +domain+ to be replicated to +mindevcount+
182   # devices.  Returns nil on failure.
184   def update_class(domain, klass, mindevcount)
185     return modify_class(domain, klass, mindevcount, :update)
186   end
188   ##
189   # Removes class +klass+ from +domain+.  Returns true if successful, false if
190   # not.
192   def delete_class(domain, klass)
193     res = @backend.delete_class :domain => domain, :class => klass
194     return !res.nil?
195   end
197   ##
198   # Creates a new host named +host+.  +args+ must contain :ip and :port.
199   # Returns true if successful, false if not.
201   def create_host(host, args = {})
202     raise ArgumentError, "Must specify ip and port" unless \
203       args.include? :ip and args.include? :port
205     return modify_host(host, args, 'create')
206   end
208   ##
209   # Updates +host+ with +args+.  Returns true if successful, false if not.
211   def update_host(host, args = {})
212     return modify_host(host, args, 'update')
213   end
215   ##
216   # Deletes host +host+.  Returns nil on failure.
218   def delete_host(host)
219     raise 'readonly mogilefs' if readonly?
220     res = @backend.delete_host :host => host
221     return !res.nil?
222   end
224   ##
225   # Changes the device status of +device+ on +host+ to +state+ which can be
226   # 'alive', 'down', or 'dead'.
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
234   protected unless defined? $TESTING
236   ##
237   # Modifies +klass+ on +domain+ to store files on +mindevcount+ devices via
238   # +action+.  Returns the class name if successful, nil if not.
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)
245     return res['class'] unless res.nil?
246   end
248   ##
249   # Modifies +host+ using +args+ via +action+.  Returns true if successful,
250   # false if not.
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
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"=>""}]
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