admin: update rdoc for deleting a domain
[ruby-mogilefs-client.git] / lib / mogilefs / admin.rb
blob5dc01d8e75999848d316110c09a548c65ce895b9
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
14     high = nil
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 }
21       low = high + 1
22     end
23   end
25   ##
26   # Returns an Array of host status Hashes.  If +hostid+ is given only that
27   # host is returned.
28   #
29   #   admin.get_hosts 1
30   #
31   # Returns:
32   #
33   #   [{"status"=>"alive",
34   #     "http_get_port"=>"",
35   #     "http_port"=>"",
36   #     "hostid"=>"1",
37   #     "hostip"=>"",
38   #     "hostname"=>"rur-1",
39   #     "remoteroot"=>"/mnt/mogilefs/rur-1",
40   #     "altip"=>"",
41   #     "altmask"=>""}]
43   def get_hosts(hostid = nil)
44     clean('hosts', 'host',
45           @backend.get_hosts(hostid ? { :hostid => hostid } : {}))
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     clean('devices', 'dev',
66           @backend.get_devices(devid ? { :devid => devid } : {}))
67   end
69   ##
70   # Returns an Array of fid Hashes from +from_fid+ to +to_fid+.
71   #
72   #   admin.list_fids 0, 100
73   #
74   # Returns:
75   #
76   #   [{"fid"=>"99",
77   #     "class"=>"normal",
78   #     "domain"=>"test",
79   #     "devcount"=>"2",
80   #     "length"=>"4",
81   #     "key"=>"file_key"},
82   #    {"fid"=>"82",
83   #     "class"=>"normal",
84   #     "devcount"=>"2",
85   #     "domain"=>"test",
86   #     "length"=>"9",
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))
92   end
94   ##
95   # Returns a statistics structure representing the state of mogilefs.
96   #
97   #   admin.get_stats
98   #
99   # Returns:
100   #
101   #   {"fids"=>{"max"=>"99", "count"=>"2"},
102   #    "device"=>
103   #     [{"status"=>"alive", "files"=>"2", "id"=>"1", "host"=>"rur-1"},
104   #      {"status"=>"alive", "files"=>"2", "id"=>"2", "host"=>"rur-2"}],
105   #    "replication"=>
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
111     stats = {}
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
118       stats['fids'] = {
119         'max' => res['fidmax'].to_i,
120         'count' => res['fidcount'].to_i
121       }
122     end
124     stats.delete 'device' if stats['device'].empty?
125     stats.delete 'file' if stats['file'].empty?
126     stats.delete 'replication' if stats['replication'].empty?
128     stats
129   end
131   ##
132   # Returns the domains and classes, and their policies present in the mogilefs.
133   #
134   #   admin.get_domains
135   #
136   # Returns (on newer MogileFS servers):
137   #   {
138   #     "test" => {
139   #       "default" => {
140   #         "mindevcount" => 2,
141   #         "replpolicy" => "MultipleHosts()"
142   #       }
143   #     }
144   #   }
145   #
146   # Returns (on older MogileFS servers without replication policies):
147   #
148   #   {"test"=>{"normal"=>3, "default"=>2}}
150   def get_domains
151     res = @backend.get_domains
152     have_replpolicy = false
154     domains = {}
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}"]] = {}
159       domain.each do |d|
160         tmp[d.delete("name")] = d
161         d["mindevcount"] = d["mindevcount"].to_i
162         have_replpolicy ||= d.include?("replpolicy")
163       end
164     end
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"]
171         end
172       end
173     end
175     domains
176   end
178   ##
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
185   end
187   ##
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?
194   end
196   ##
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)
202   end
204   ##
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)
210   end
212   ##
213   # Removes class +klass+ from +domain+.  Returns true if successful, false if
214   # not.
216   def delete_class(domain, klass)
217     ! @backend.delete_class(:domain => domain, :class => klass).nil?
218   end
220   ##
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')
229   end
231   ##
232   # Updates +host+ with +args+.  Returns true if successful, false if not.
234   def update_host(host, args = {})
235     modify_host(host, args, 'update')
236   end
238   ##
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?
244   end
246   ##
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?
253   end
255   protected unless defined? $TESTING
257   ##
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
267   end
269   ##
270   # Modifies +host+ using +args+ via +action+.  Returns true if successful,
271   # false if not.
273   def modify_host(host, args = {}, action = 'create')
274     args[:host] = host
275     ! @backend.send("#{action}_host", args).nil?
276   end
278   ##
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.
282   #
283   #   res = {"host1_remoteroot"=>"/mnt/mogilefs/rur-1",
284   #          "host1_hostname"=>"rur-1",
285   #          "host1_hostid"=>"1",
286   #          "host1_http_get_port"=>"",
287   #          "host1_altip"=>"",
288   #          "hosts"=>"1",
289   #          "host1_hostip"=>"",
290   #          "host1_http_port"=>"",
291   #          "host1_status"=>"alive",
292   #          "host1_altmask"=>""}
293   #   admin.clean 'hosts', 'host', res
294   #
295   # Returns:
296   #
297   #   [{"status"=>"alive",
298   #     "http_get_port"=>"",
299   #     "http_port"=>"",
300   #     "hostid"=>"1",
301   #     "hostip"=>"",
302   #     "hostname"=>"rur-1",
303   #     "remoteroot"=>"/mnt/mogilefs/rur-1",
304   #     "altip"=>"",
305   #     "altmask"=>""}]
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]
312       end
313       Hash[*dev.flatten]
314     end
315   end