Restructure library plug-ins again.
[virt-df.git] / lib / diskimage_lvm2.ml
1 (* 'df' command for virtual domains.
2
3    (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
4    http://libvirt.org/
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20    Support for LVM2 PVs.
21 *)
22
23 open Printf
24 open ExtList
25
26 open Diskimage_impl
27 open Diskimage_lvm2_metadata
28
29 open Int63.Operators
30
31 let id = "LVM2"
32
33 let sector_size_int = 512
34 let sector_size = ~^sector_size_int
35
36 (*----------------------------------------------------------------------*)
37 (* Block device which can do linear maps, same as the kernel dm-linear.c *)
38 class linear_map_device name extent_size segments =
39   (* The segments are passed containing (start_extent, extent_count, ...)
40    * but it's easier to deal with (start_extent, end_extent, ...) so
41    * rewrite them.
42    *)
43   let segments = List.map
44     (fun (start_extent, extent_count, dev, pvoffset) ->
45        (start_extent, start_extent +^ extent_count, dev, pvoffset)
46     ) segments in
47
48   (* Calculate the size of the device (in bytes).  Note that because
49    * of the random nature of the mapping this doesn't imply that we can
50    * satisfy any read request up to the full size.
51    *)
52   let size_in_extents =
53     List.fold_left max ~^0
54       (List.map (fun (_, end_extent, _, _) -> end_extent) segments) in
55   let size = size_in_extents *^ extent_size in
56 object (self)
57   inherit device
58   method name = name
59   method size = size
60
61   (* The natural blocksize for LVM devices is the extent size.
62    * NB. Throws a runtime exception if the extent size is bigger
63    * than an int (only likely to matter on 32 bit).
64    *)
65   method blocksize = extent_size
66
67   method private map i =
68     if i < ~^0 || i >= size_in_extents then
69       invalid_arg "linear_map_device: read outside device";
70
71     let rec loop = function
72       | [] ->
73           None
74       | (start_extent, end_extent, dev, pvoffset) :: rest ->
75           if start_extent <= i && i < end_extent then (
76             let dev_offset = (pvoffset +^ i) *^ extent_size in
77             Some (start_extent, end_extent, dev, dev_offset, pvoffset)
78           ) else
79             loop rest
80     in
81     loop segments
82
83   (* Map block (extent) i to the underlying device. *)
84   method map_block i =
85     match self#map i with
86     | Some (_, _, dev, dev_offset, _) -> [dev, dev_offset]
87     | None -> []
88
89   (* Continguous span. *)
90   method contiguous offset =
91     let offset_in_extents = offset /^ extent_size in
92
93     (* Get the segment that this offset lies in. *)
94     match self#map offset_in_extents with
95     | Some (_, end_extent, dev, dev_offset, _) ->
96         (* Contiguous bytes up to the end of this extent. *)
97         end_extent *^ extent_size -^ offset
98     | None -> ~^0
99
100   (* NB. Use the superclass #read method. *)
101 end
102
103 (*----------------------------------------------------------------------*)
104 (* Probe to see if it's an LVM2 PV. *)
105 let rec probe dev =
106   try
107     let uuid, _ = read_pv_label dev in
108     if !debug then
109       eprintf "LVM2 detected PV UUID %s\n%!" uuid;
110     { pv_cb = callbacks; pv_uuid = uuid; pv_dev = dev }
111   with exn ->
112     if !debug then prerr_endline (Printexc.to_string exn);
113     raise Not_found
114
115 and read_pv_label dev =
116   (* Load the first 8 sectors.  I found by experimentation that
117    * the second sector contains the header ("LABELONE" etc) and
118    * the nineth sector contains some additional information about
119    * the location of the current metadata.
120    *)
121   let bits = dev#read_bitstring ~^0 (~^9 *^ sector_size) in
122
123   (*Bitmatch.hexdump_bitstring stdout bits;*)
124
125   bitmatch bits with
126   | {
127       (* sector 0 *)
128       sector0 : sector_size_int*8 : bitstring;
129
130       (* sector 1 *)
131       "LABELONE" : 64 : string;         (* "LABELONE" *)
132       _ : 128 : bitstring;              (* Seems to contain something. *)
133       "LVM2 001" : 64 : string;         (* "LVM2 001" *)
134       uuid : 256 : string;              (* UUID *)
135       endsect : (sector_size_int-64)*8 : bitstring;(* to end of second sector *)
136
137       (* sectors 2-7 *)
138       sectors234567 : sector_size_int*8 * 6 : bitstring;
139
140       (* sector 8 *)
141       _ : 320 : bitstring;              (* start of sector 8 *)
142       metadata_offset : 32 : littleendian; (* metadata offset *)
143       _ : 32 : bitstring;
144       metadata_length : 32 : littleendian (* length of metadata (bytes) *)
145     } ->
146
147       (* Metadata offset is relative to end of PV label. *)
148       let metadata_offset = Int63.of_int32 metadata_offset +^ ~^0x1000 in
149       (* Metadata length appears to include the trailing \000 which
150        * we don't want.
151        *)
152       let metadata_length = Int63.of_int32 metadata_length -^ ~^1 in
153
154       let metadata = read_metadata dev metadata_offset metadata_length in
155
156       uuid, metadata
157
158   | { _ } ->
159       invalid_arg
160         (sprintf "LVM2: read_pv_label: %s: not an LVM2 physical volume"
161            dev#name)
162
163 and read_metadata dev offset len =
164   if !debug then
165     eprintf "metadata: offset %s len %s bytes\n%!"
166       (Int63.to_string offset) (Int63.to_string len);
167
168   (* Check the offset and length are sensible. *)
169   if offset <= ~^0x1200 || offset >= dev#size
170     || len <= ~^0 || offset +^ len >= dev#size then
171       invalid_arg "LVM2: read_metadata: bad metadata offset or length";
172
173   (* If it is outside the disk boundaries, this will throw an exception,
174    * otherwise it will read and return the metadata string.
175    *)
176   dev#read offset len
177
178 (*----------------------------------------------------------------------*)
179 (* We are passed a list of devices which we previously identified
180  * as PVs belonging to us.  From these produce a list of all LVs
181  * (as devices) and return them.  Note that we don't try to detect
182  * what is on these LVs - that will be done in the main code.
183  *)
184 and list_lvs pvs =
185   (* Read the UUID and metadata (again) from each device to end up with
186    * an assoc list of PVs, keyed on the UUID.
187    *
188    * XXX We've already read this - we should save it in the pv struct.
189    *)
190   let pvs = List.map (
191     fun { pv_dev = dev } ->
192       let uuid, metadata = read_pv_label dev in
193       (uuid, (metadata, dev))
194   ) pvs in
195
196   (* Parse the metadata using the external lexer/parser. *)
197   let pvs = List.map (
198     fun (uuid, (metadata, dev)) ->
199       uuid, (Diskimage_lvm2_lexer.parse_lvm2_metadata_from_string metadata,
200              dev)
201   ) pvs in
202
203   (* Print the parsed metadata. *)
204   if !debug then
205     List.iter (
206       fun (uuid, (metadata, dev)) ->
207         eprintf "metadata for PV UUID %s on %s:\n" uuid dev#name;
208         output_metadata stderr metadata
209     ) pvs;
210
211   (* Scan for volume groups.  The first entry in the metadata
212    * appears to be the volume group name.  This gives us a
213    * list of VGs and the metadata for each underlying PV.
214    *)
215   let vgnames =
216     List.filter_map (
217       function
218       | pvuuid, (((vgname, Metadata vgmeta) :: _), dev) ->
219           Some (vgname, (pvuuid, vgmeta))
220       | _ -> None
221     ) pvs in
222
223   let cmp ((a:string),_) ((b:string),_) = compare a b in
224   let vgnames = List.sort ~cmp vgnames in
225   let vgs = group_by vgnames in
226
227   (* Note that the metadata is supposed to be duplicated
228    * identically across all PVs (for redundancy purposes).
229    * In theory we should check this and use the 'seqno'
230    * field to find the latest metadata if it doesn't match,
231    * but in fact we don't check this.
232    *)
233   let vgs = List.map (
234     fun (vgname, metas) ->
235       let pvuuids = List.map fst metas in
236       let _, vgmeta = List.hd metas in (* just pick any metadata *)
237       vgname, (pvuuids, vgmeta)) vgs in
238
239   (* Print the VGs. *)
240   if !debug then
241     List.iter (
242       fun (vgname, (pvuuids, vgmeta)) ->
243         eprintf "VG %s is on PVs: %s\n%!" vgname (String.concat "," pvuuids)
244     ) vgs;
245
246   (* Some useful getter functions.  If these can't get a value
247    * from the metadata or if the type is wrong they raise Not_found.
248    *)
249   let rec get_int63 field meta =
250     match List.assoc field meta with
251     | Int i -> i
252     | _ -> raise Not_found
253   and get_int_bounded field meta max =
254     match List.assoc field meta with
255     | Int i when i >= ~^0 && i <= Int63.of_int max -> Int63.to_int i
256     | _ -> raise Not_found
257   and get_string field meta =
258     match List.assoc field meta with
259     | String s -> s
260     | _ -> raise Not_found
261   and get_meta field meta =
262     match List.assoc field meta with
263     | Metadata md -> md
264     | _ -> raise Not_found
265   and get_stripes field meta =          (* List of (string,int) pairs. *)
266     match List.assoc field meta with
267     | List xs ->
268         let rec loop = function
269           | [] -> []
270           | String pvname :: Int offset :: xs ->
271               (pvname, offset) :: loop xs
272           | _ -> raise Not_found
273         in
274         loop xs
275     | _ -> raise Not_found
276   in
277
278   (* The volume groups refer to the physical volumes using their
279    * own naming system ("pv0", "pv1", etc.) instead of PV UUIDs.
280    *
281    * Each PV also has a start (in sectors) & count (in extents)
282    * of the writable area (the bit after the superblock and metadata)
283    * which normally starts at sector 384.
284    *
285    * Create a PV device (simple offset + size) and a map from PV
286    * names to these devices.
287    *)
288   let vgs = List.map (
289     fun (vgname, (pvuuids, vgmeta)) ->
290       let pvdevs, extent_size =
291         try
292           (* NB: extent_size is in sectors here - we convert to bytes. *)
293           let extent_size =
294             get_int_bounded "extent_size" vgmeta (1024*1024) in
295           let extent_size = Int63.of_int extent_size in
296           let extent_size = extent_size *^ sector_size in
297
298           (* Get the physical_volumes section of the metadata. *)
299           let pvdevs = get_meta "physical_volumes" vgmeta in
300
301           List.filter_map (
302             function
303             | (pvname, Metadata meta) ->
304                 (* Get the UUID. *)
305                 let pvuuid = get_string "id" meta in
306                 let pvuuid = canonical_uuid pvuuid in
307
308                 (* Get the underlying physical device. *)
309                 let _, dev = List.assoc pvuuid pvs in
310
311                 (* Construct a PV device. *)
312                 let pe_start = get_int63 "pe_start" meta in
313                 let pe_start = pe_start *^ sector_size in
314                 let pe_count = get_int63 "pe_count" meta in
315                 let pe_count = pe_count *^ extent_size in
316                 let pvdev =
317                   new offset_device
318                     pvuuid (* name *)
319                     pe_start pe_count (* start, size in bytes *)
320                     (* don't really have a natural block size ... *)
321                     extent_size
322                     dev (* underlying device *) in
323
324                 Some (pvname, pvdev)
325             | _ ->
326                 None
327           ) pvdevs, extent_size
328         with
329           (* Something went wrong - just return an empty map. *)
330           Not_found -> [], ~^0 in
331       (vgname, (pvuuids, vgmeta, pvdevs, extent_size))
332   ) vgs in
333
334   (* Scan for logical volumes.  Each VG contains several LVs.
335    * This gives us a list of LVs within each VG (hence extends
336    * the vgs variable).
337    *)
338   let vgs = List.map (
339     fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size)) ->
340       let lvs =
341         try
342           let lvs = get_meta "logical_volumes" vgmeta in
343           let lvs = List.filter_map (
344             function
345             | lvname, Metadata lvmeta ->
346                 (try
347                    let segment_count =
348                      get_int_bounded "segment_count" lvmeta 1024 in
349
350                    (* Get the segments for this LV. *)
351                    let segments = range 1 (segment_count+1) in
352                    let segments =
353                      List.map
354                        (fun i -> get_meta ("segment" ^ string_of_int i) lvmeta)
355                        segments in
356
357                    let segments =
358                      List.map (
359                        fun segmeta ->
360                          let start_extent =
361                            get_int63 "start_extent" segmeta in
362                          let extent_count =
363                            get_int63 "extent_count" segmeta in
364                          let segtype = get_string "type" segmeta in
365
366                          (* Can only handle striped segments at the
367                           * moment. XXX
368                           *)
369                          if segtype <> "striped" then raise Not_found;
370
371                          let stripe_count =
372                            get_int_bounded "stripe_count" segmeta 1024 in
373                          let stripes = get_stripes "stripes" segmeta in
374
375                          if List.length stripes <> stripe_count then
376                            raise Not_found;
377
378                          (* Can only handle linear striped segments at
379                           * the moment. XXX
380                           *)
381                          if stripe_count <> 1 then raise Not_found;
382                          let pvname, pvoffset = List.hd stripes in
383
384                          (start_extent, extent_count, pvname, pvoffset)
385                      ) segments in
386
387                    Some (lvname, segments)
388                  with
389                    (* Something went wrong with segments - omit this LV. *)
390                    Not_found -> None)
391             | _ -> None
392           ) lvs in
393
394           lvs
395         with
396           Not_found ->
397             (* Something went wrong - assume no LVs found. *)
398             [] in
399       (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs))
400   ) vgs in
401
402   (* Print the LVs. *)
403   if !debug then (
404     List.iter (
405       fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) ->
406         eprintf "VG %s: (extent_size = %s bytes)\n" vgname
407           (Int63.to_string extent_size);
408         List.iter (
409           fun (lvname, segments) ->
410             eprintf "  %s/%s:\n" vgname lvname;
411             List.iter (
412               fun (start_extent, extent_count, pvname, pvoffset) ->
413                 eprintf "    start %s count %s at %s:%s\n"
414                   (Int63.to_string start_extent)
415                   (Int63.to_string extent_count)
416                   pvname (Int63.to_string pvoffset)
417             ) segments
418         ) lvs
419     ) vgs;
420     flush stderr
421   );
422
423   (* Finally we can set up devices for the LVs. *)
424   let lvs =
425     List.map (
426       fun (vgname, (pvuuid, vgmeta, pvdevs, extent_size, lvs)) ->
427         try
428           List.map (
429             fun (lvname, segments) ->
430               let name = vgname ^ "/" ^ lvname in
431               let segments = List.map (
432                 fun (start_extent, extent_count, pvname, pvoffset) ->
433                   (* Get the PV device. *)
434                   let pvdev = List.assoc pvname pvdevs in
435
436                   (* Extents                 mapped to:             *)
437                   (start_extent, extent_count,          pvdev, pvoffset)
438               ) segments in
439
440               (* Create a linear mapping device. *)
441               let lv_dev = new linear_map_device name extent_size segments in
442
443               { lv_dev = lv_dev }
444           ) lvs
445         with
446           Not_found -> []
447     ) vgs in
448   let lvs = List.concat lvs in
449
450   (* Return the list of LV devices. *)
451   lvs
452
453 (* XXX We don't currently have enough information in the PV
454  * structure to determine quickly which blocks are used.  Need
455  * to store the parsed metadata in the structure ...
456  *)
457 and offset_is_free _ _ = false
458
459 and callbacks = {
460   lvm_cb_name = id;
461   lvm_cb_list_lvs = list_lvs;
462   lvm_cb_offset_is_free = offset_is_free;
463 }
464
465 (* Register the plugin. *)
466 let () = register_plugin ~lvm:probe id