Updated MANIFEST.
[virt-top.git] / virt-df / virt_df_main.ml
index 9504785..65d1f2f 100644 (file)
@@ -54,6 +54,8 @@ let () =
       "uri " ^ s_ "Connect to URI (default: Xen)";
     "--connect", Arg.String set_uri,
       "uri " ^ s_ "Connect to URI (default: Xen)";
+    "--debug", Arg.Set debug,
+      " " ^ s_ "Debug mode (default: false)";
     "-h", Arg.Set human,
       " " ^ s_ "Print sizes in human-readable format";
     "--human-readable", Arg.Set human,
@@ -63,7 +65,7 @@ let () =
     "--inodes", Arg.Set inodes,
       " " ^ s_ "Show inodes instead of blocks";
     "-t", Arg.String test_mode,
-      "dev" ^ s_ "(Test mode) Display contents of block device or file";
+      "dev " ^ s_ "(Test mode) Display contents of block device or file";
     "--version", Arg.Unit version,
       " " ^ s_ "Display version and exit";
   ] in
@@ -223,7 +225,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 +244,8 @@ OPTIONS" in
                d_source = filename; d_target = "hda";
                d_dev = new block_device filename; d_content = `Unknown;
              }
-           ]
+           ];
+           dom_lv_filesystems = []
          }
       ) !test_files
     ) in
@@ -268,13 +272,18 @@ OPTIONS" in
          { disk with d_content = `Partitions parts }
       | None ->
          (* Not partitioned.  Does it contain a filesystem? *)
-         let fs = probe_for_filesystems dev in
+         let fs = probe_for_filesystem dev in
          match fs with
          | Some fs ->
              { disk with d_content = `Filesystem fs }
          | None ->
-             (* Not partitioned, no filesystem, so it's spare. *)
-             disk
+             (* Not partitioned, no filesystem, is it a PV? *)
+             let pv = probe_for_pv dev in
+             match pv with
+             | Some lvm_name ->
+                 { disk with d_content = `PhysicalVolume lvm_name }
+             | None ->
+                 disk (* Spare/unknown. *)
   ) in
 
   (* Now we have either detected partitions or a filesystem on each
@@ -287,12 +296,18 @@ OPTIONS" in
        let ps = List.map (
          fun p ->
            if p.part_status = Bootable || p.part_status = Nonbootable then (
-             let fs = probe_for_filesystems p.part_dev in
+             let fs = probe_for_filesystem p.part_dev in
              match fs with
              | Some fs ->
                  { p with part_content = `Filesystem fs }
              | None ->
-                 p
+                 (* Is it a PV? *)
+                 let pv = probe_for_pv p.part_dev in
+                 match pv with
+                 | Some lvm_name ->
+                     { p with part_content = `PhysicalVolume lvm_name }
+                 | None ->
+                     p (* Spare/unknown. *)
            ) else p
        ) parts.parts in
        let parts = { parts with parts = ps } in
@@ -300,11 +315,76 @@ OPTIONS" in
     | disk -> disk
   ) in
 
-  (* XXX LVM stuff here. *)
-
-
-
-  (* Print the title. *)
+  (* 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).
+   *
+   * XXX To deal with RAID (ie. md devices) we will need to loop
+   * around here because RAID is like LVM except that they normally
+   * present as block devices which can be used by LVM.
+   *)
+  (* 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 pv } -> Some (pv, 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 pv } ->
+                   Some (pv, 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,_) (b,_) = compare a b in
+      let lvs = List.sort ~cmp lvs in
+      let lvs = group_by lvs in
+
+      let lvs =
+       List.map (fun (pv, devs) -> list_lvs pv.lvm_plugin_id 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 (
+         fun ({ lv_dev = dev } as lv) ->
+           match probe_for_filesystem dev with
+           | Some fs -> Some (lv, fs)
+           | None -> None
+       ) lvs in
+
+      { dom with dom_lv_filesystems = filesystems }
+  ) doms in
+
+  (* Now print the results.
+   *
+   * Print the title.
+   *)
   let () =
     let total, used, avail =
       match !inodes, !human with
@@ -324,37 +404,49 @@ OPTIONS" in
   in
 
   (* HOF to iterate over filesystems. *)
-  let iter_over_filesystems doms f =
+  let iter_over_filesystems doms
+      (f : domain -> ?disk:disk -> ?partno:int -> device -> 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
+         | ({ d_content = `Filesystem fs; d_dev = dev } as disk) ->
+             f dom ~disk dev 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
+                 | { part_content = `Filesystem fs; part_dev = dev } ->
+                     f dom ~disk ~partno:(i+1) dev fs
                  | _ -> ()
              ) partitions.parts
          | _ -> ()
-       ) disks
+       ) disks;
+       (* LV filesystems. *)
+       List.iter (fun ({lv_dev = dev}, fs) -> f dom dev fs) filesystems
     ) doms
   in
 
   (* Print stats for each recognized filesystem. *)
-  let print_stats dom disk part fs =
+  let print_stats dom ?disk ?partno dev 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
-      match part with
+      (* Get the disk name (eg. "hda") from the domain XML, if
+       * we have it, otherwise use the device name (eg. for LVM).
+       *)
+      let disk_name =
+       match disk with
+       | None -> dev#name
+       | Some disk -> disk.d_target
+      in
+      match partno with
       | None ->
-         dom_name ^ ":" ^ d_target
-      | Some (_, pnum) ->
-         dom_name ^ ":" ^ d_target ^ string_of_int pnum in
+         dom_name ^ ":" ^ disk_name
+      | Some partno ->
+         dom_name ^ ":" ^ disk_name ^ string_of_int partno in
     printf "%-20s " name;
 
     if fs.fs_is_swap then (