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