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