From be71668a1a4b6c87da3e82458ca97a199a24aa32 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 1 Jan 1970 00:00:00 +0000 Subject: [PATCH] Infrastructure to detect filesystems on LVs. --- virt-df/virt_df.ml | 24 +++++++++++++ virt-df/virt_df.mli | 17 +++++++-- virt-df/virt_df_main.ml | 96 ++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 119 insertions(+), 18 deletions(-) diff --git a/virt-df/virt_df.ml b/virt-df/virt_df.ml index b992e1b..1cd0617 100644 --- a/virt-df/virt_df.ml +++ b/virt-df/virt_df.ml @@ -81,6 +81,7 @@ type domain = { dom_name : string; (* Domain name. *) dom_id : int option; (* Domain ID (if running). *) dom_disks : disk list; (* Domain disks. *) + dom_lv_filesystems : filesystem list; (* Domain LV filesystems. *) } and disk = { (* From the XML ... *) @@ -220,3 +221,26 @@ let probe_for_pv dev = eprintf "%s contains a %s PV\n%!" dev#name lvm_name ); r + +let list_lvs lvm_name devs = + let _, list_lvs_fn = List.assoc lvm_name !lvm_types in + list_lvs_fn devs + +(*----------------------------------------------------------------------*) + +(* This version by Isaac Trotts. *) +let group_by ?(cmp = Pervasives.compare) ls = + let ls' = + List.fold_left + (fun acc (day1, x1) -> + match acc with + [] -> [day1, [x1]] + | (day2, ls2) :: acctl -> + if cmp day1 day2 = 0 + then (day1, x1 :: ls2) :: acctl + else (day1, [x1]) :: acc) + [] + ls + in + let ls' = List.rev ls' in + List.map (fun (x, xs) -> x, List.rev xs) ls' diff --git a/virt-df/virt_df.mli b/virt-df/virt_df.mli index db98af2..4a9368c 100644 --- a/virt-df/virt_df.mli +++ b/virt-df/virt_df.mli @@ -119,6 +119,7 @@ type domain = { dom_name : string; (** Domain name. *) dom_id : int option; (** Domain ID (if running). *) dom_disks : disk list; (** Domain disks. *) + dom_lv_filesystems : filesystem list; (** Domain LV filesystems. *) } and disk = { d_type : string option; (** The *) @@ -168,14 +169,16 @@ val string_of_partition : partition -> string val string_of_filesystem : filesystem -> string (** Convert a partition or filesystem struct to a string (for debugging). *) +(** {2 Plug-in registration functions} *) + val partition_type_register : string -> (device -> partitions) -> unit -(** Register a partition probing plugin. *) +(** Register a partition probing plug-in. *) val probe_for_partitions : device -> partitions option (** Do a partition probe on a device. Returns [Some partitions] or [None]. *) val filesystem_type_register : string -> (device -> filesystem) -> unit -(** Register a filesystem probing plugin. *) +(** Register a filesystem probing plug-in. *) val probe_for_filesystem : device -> filesystem option (** Do a filesystem probe on a device. Returns [Some filesystem] or [None]. *) @@ -191,3 +194,13 @@ val lvm_type_register : val probe_for_pv : device -> string option (** Do a PV probe on a device. Returns [Some lvm_name] or [None]. *) + +val list_lvs : string -> device list -> device list +(** Construct LV devices from a list of PVs. The first argument + is the [lvm_name] which all PVs should belong to. +*) + +(** {2 Utility functions} *) + +val group_by : ?cmp:('a -> 'a -> int) -> ('a * 'b) list -> ('a * 'b list) list +(** Group a sorted list of pairs by the first element of the pair. *) diff --git a/virt-df/virt_df_main.ml b/virt-df/virt_df_main.ml index c989d76..82fe920 100644 --- a/virt-df/virt_df_main.ml +++ b/virt-df/virt_df_main.ml @@ -223,7 +223,8 @@ OPTIONS" in | _ -> None ) devices in - { dom_name = name; dom_id = domid; dom_disks = disks } + { dom_name = name; dom_id = domid; + dom_disks = disks; dom_lv_filesystems = [] } ) xmls ) else ( (* In test mode (-t option) the user can pass one or more @@ -241,7 +242,8 @@ OPTIONS" in d_source = filename; d_target = "hda"; d_dev = new block_device filename; d_content = `Unknown; } - ] + ]; + dom_lv_filesystems = [] } ) !test_files ) in @@ -311,13 +313,66 @@ OPTIONS" in | disk -> disk ) in - (* XXX LVM filesystem detection ... *) - - - + (* LVM filesystem detection + * + * For each domain, look for all disks/partitions which have been + * identified as PVs and pass those back to the respective LVM + * plugin for LV detection. + * + * (Note - a two-stage process because an LV can be spread over + * several PVs, so we have to detect all PVs belonging to a + * domain first). + *) + (* First: LV detection. *) + let doms = List.map ( + fun ({ dom_disks = disks } as dom) -> + (* Find all physical volumes, can be disks or partitions. *) + let pvs_on_disks = List.filter_map ( + function + | { d_dev = d_dev; + d_content = `PhysicalVolume lvm_name } -> Some (lvm_name, d_dev) + | _ -> None + ) disks in + let pvs_on_partitions = List.map ( + function + | { d_content = `Partitions { parts = parts } } -> + List.filter_map ( + function + | { part_dev = part_dev; + part_content = `PhysicalVolume lvm_name } -> + Some (lvm_name, part_dev) + | _ -> None + ) parts + | _ -> [] + ) disks in + let lvs = List.concat (pvs_on_disks :: pvs_on_partitions) in + dom, lvs + ) doms in + + (* Second: filesystem on LV detection. *) + let doms = List.map ( + fun (dom, lvs) -> + (* Group the LVs by plug-in type. *) + let cmp ((a:string),_) ((b:string),_) = compare a b in + let lvs = List.sort ~cmp lvs in + let lvs = group_by lvs in + + let lvs = + List.map (fun (lvm_name, devs) -> list_lvs lvm_name devs) lvs in + let lvs = List.concat lvs in + + (* lvs is a list of potential LV devices. Now run them through the + * probes to see if any contain filesystems. + *) + let filesystems = List.filter_map probe_for_filesystem lvs in + { dom with dom_lv_filesystems = filesystems } + ) doms in - (* Print the title. *) + (* Now print the results. + * + * Print the title. + *) let () = let total, used, avail = match !inodes, !human with @@ -337,37 +392,46 @@ OPTIONS" in in (* HOF to iterate over filesystems. *) - let iter_over_filesystems doms f = + let iter_over_filesystems doms + (f : domain -> ?disk:disk -> ?part:(partition * int) -> filesystem -> + unit) = List.iter ( - fun ({ dom_disks = disks } as dom) -> + fun ({ dom_disks = disks; dom_lv_filesystems = filesystems } as dom) -> + (* Ordinary filesystems found on disks & partitions. *) List.iter ( function | ({ d_content = `Filesystem fs } as disk) -> - f dom disk None fs + f dom ~disk fs | ({ d_content = `Partitions partitions } as disk) -> List.iteri ( fun i -> function | ({ part_content = `Filesystem fs } as part) -> - f dom disk (Some (part, i)) fs + f dom ~disk ~part:(part, i) fs | _ -> () ) partitions.parts | _ -> () - ) disks + ) disks; + (* LV filesystems. *) + List.iter (fun fs -> f dom fs) filesystems ) doms in (* Print stats for each recognized filesystem. *) - let print_stats dom disk part fs = + let print_stats dom ?disk ?part fs = (* Printable name is like "domain:hda" or "domain:hda1". *) let name = let dom_name = dom.dom_name in - let d_target = disk.d_target in + let disk_name = + match disk with + | None -> "???" (* XXX keep LV dev around *) + | Some disk -> disk.d_target + in match part with | None -> - dom_name ^ ":" ^ d_target + dom_name ^ ":" ^ disk_name | Some (_, pnum) -> - dom_name ^ ":" ^ d_target ^ string_of_int pnum in + dom_name ^ ":" ^ disk_name ^ string_of_int pnum in printf "%-20s " name; if fs.fs_is_swap then ( -- 1.8.3.1