diff --git a/Ganeti/HTools/Cluster.hs b/Ganeti/HTools/Cluster.hs
index 91ef098fa52bda53e4ffe000deb204dbd23b1f6f..ab21b3240bed628bd8802e59686db5eb11a6cf86 100644
--- a/Ganeti/HTools/Cluster.hs
+++ b/Ganeti/HTools/Cluster.hs
@@ -687,34 +687,29 @@ printSolution nl il sol =
 printNodes :: Node.List -> String
 printNodes nl =
     let snl = sortBy (compare `on` Node.idx) (Container.elems nl)
-        m_name = maximum . map (length . Node.name) $ snl
-        helper = Node.list m_name
-        h2 = printf " %5s %5s %5s %5s" "lCpu" "lMem" "lDsk" "lNet"::String
-        header = printf
-                 "%2s %-*s %5s %5s %5s %5s %5s %5s %5s %5s %4s %4s \
-                 \%3s %3s %6s %6s %5s"
-                 " F" m_name "Name"
-                 "t_mem" "n_mem" "i_mem" "x_mem" "f_mem" "r_mem"
-                 "t_dsk" "f_dsk" "pcpu" "vcpu"
-                 "pri" "sec" "p_fmem" "p_fdsk" "r_cpu"::String
-    in unlines ((header++h2):map helper snl)
+        header = ["F", "Name"
+                 , "t_mem", "n_mem", "i_mem", "x_mem", "f_mem", "r_mem"
+                 , "t_dsk", "f_dsk", "pcpu", "vcpu", "pri",  "sec"
+                 , "p_fmem", "p_fdsk", "r_cpu"
+                 , "lCpu", "lMem", "lDsk", "lNet" ]
+        isnum = False:False:repeat True
+    in unlines . map ((:) ' ' .  intercalate " ") $
+       formatTable (header:map Node.list snl) isnum
 
 -- | Print the instance list.
 printInsts :: Node.List -> Instance.List -> String
 printInsts nl il =
     let sil = sortBy (compare `on` Instance.idx) (Container.elems il)
-        m_name = maximum . map (length . Instance.name) $ sil
-        m_nnm  = maximum . map (length . Node.name) $ Container.elems nl
-        helper inst = printf "%2s %-*s %-*s %-*s"
-                      "  " m_name (Instance.name inst)
-                      m_nnm (Container.nameOf nl (Instance.pNode inst))
-                      m_nnm (let sdx = Instance.sNode inst
-                             in if sdx == Node.noSecondary
-                                then  ""
-                                else Container.nameOf nl sdx)
-        header = printf "%2s %-*s %-*s %-*s"
-                 "  " m_name "Name" m_nnm "Pri_node" m_nnm "Sec_node"::String
-    in unlines (header:map helper sil)
+        helper inst = [ (Instance.name inst)
+                      , (Container.nameOf nl (Instance.pNode inst))
+                      , (let sdx = Instance.sNode inst
+                         in if sdx == Node.noSecondary
+                            then  ""
+                            else Container.nameOf nl sdx) ]
+        header = ["Name", "Pri_node", "Sec_node"]
+        isnum = repeat False
+    in unlines . map ((:) ' ' . intercalate " ") $
+       formatTable (header:map helper sil) isnum
 
 -- | Shows statistics for a given node list.
 printStats :: Node.List -> String
diff --git a/Ganeti/HTools/Node.hs b/Ganeti/HTools/Node.hs
index 57d485996230ea71cd02baf03cb7976d067343d2..05f8ba1445967b8586f542ae5fb114dd4a6036db 100644
--- a/Ganeti/HTools/Node.hs
+++ b/Ganeti/HTools/Node.hs
@@ -346,33 +346,42 @@ availDisk t =
 
 -- * Display functions
 
+showField :: Node -> String -> String
+showField t field =
+    case field of
+      "name" -> name t
+      "status" -> if offline t then "-"
+                  else if failN1 t then "*" else " "
+      "tmem" -> printf "%5.0f" $ tMem t
+      "nmem" -> printf "%5d" $ nMem t
+      "xmem" -> printf "%5d" $ xMem t
+      "fmem" -> printf "%5d" $ fMem t
+      "imem" -> printf "%5d" imem
+      "rmem" -> printf "%5d" $ rMem t
+      "tdsk" -> printf "%5.0f" $ tDsk t / 1024
+      "fdsk" -> printf "%5d" $ fDsk t `div` 1024
+      "tcpu" -> printf "%4.0f" $ tCpu t
+      "ucpu" -> printf "%4d" $ uCpu t
+      "plist" -> printf "%3d" $ length (pList t)
+      "slist" -> printf "%3d" $ length (sList t)
+      "pfmem" -> printf "%6.4f" $ pMem t
+      "pfdsk" -> printf "%6.4f" $ pDsk t
+      "rcpu"  -> printf "%5.2f" $ pCpu t
+      "cload" -> printf "%5.3f" uC
+      "mload" -> printf "%5.3f" uM
+      "dload" -> printf "%5.3f" uD
+      "nload" -> printf "%5.3f" uN
+      _ -> printf "<unknown field>"
+    where
+      T.DynUtil { T.cpuWeight = uC, T.memWeight = uM,
+                  T.dskWeight = uD, T.netWeight = uN } = utilLoad t
+      imem = truncate (tMem t) - nMem t - xMem t - fMem t
+
+
 -- | String converter for the node list functionality.
-list :: Int -> Node -> String
-list mname t =
-    let pl = length $ pList t
-        sl = length $ sList t
-        mp = pMem t
-        dp = pDsk t
-        cp = pCpu t
-        off = offline t
-        fn = failN1 t
-        tmem = tMem t
-        nmem = nMem t
-        xmem = xMem t
-        fmem = fMem t
-        imem = truncate tmem - nmem - xmem - fmem
-        T.DynUtil { T.cpuWeight = uC, T.memWeight = uM,
-                    T.dskWeight = uD, T.netWeight = uN } = utilLoad t
-        wstr = printf " %5.3f %5.3f %5.3f %5.3f" uC uM uD uN::String
-    in
-      if off
-         then printf " - %-*s %57s %3d %3d"
-              mname (name t) "" pl sl
-         else
-             printf " %c %-*s %5.0f %5d %5d %5d %5d %5d %5.0f %5d\
-                    \ %4.0f %4d %3d %3d %6.4f %6.4f %5.2f"
-                 (if off then '-' else if fn then '*' else ' ')
-                 mname (name t) tmem nmem imem xmem fmem (rMem t)
-                 (tDsk t / 1024) (fDsk t `div` 1024)
-                 (tCpu t) (uCpu t)
-                 pl sl mp dp cp ++ wstr
+list :: Node -> [String]
+list t = map (showField t)
+         [ "status", "name", "tmem", "nmem", "imem", "xmem", "fmem"
+         , "rmem", "tdsk", "fdsk", "tcpu", "ucpu", "plist", "slist"
+         , "pfmem", "pfdsk", "rcpu"
+         , "cload", "mload", "dload", "nload" ]
diff --git a/Ganeti/HTools/Utils.hs b/Ganeti/HTools/Utils.hs
index 2cf8aae2c558209a0d3684a761ad3eee2d7a4215..1c51f386a75f77710fdb0e6e48ddb8eea5d17c00 100644
--- a/Ganeti/HTools/Utils.hs
+++ b/Ganeti/HTools/Utils.hs
@@ -35,6 +35,7 @@ module Ganeti.HTools.Utils
     , asObjectList
     , fromJResult
     , tryRead
+    , formatTable
     ) where
 
 import Data.List
@@ -140,3 +141,22 @@ parseChoices name s _ = fail $ name ++ ": cannot parse string '" ++ s ++ "'"
 -- | Safe 'read' function returning data encapsulated in a Result.
 tryRead :: (Monad m, Read a) => String -> String -> m a
 tryRead name s = parseChoices name s $ reads s
+
+-- | Format a table of strings to maintain consistent length
+formatTable :: [[String]] -> [Bool] -> [[String]]
+formatTable vals numpos =
+    let vtrans = transpose vals  -- transpose, so that we work on rows
+                                 -- rather than columns
+        mlens = map (maximum . map length) vtrans
+        expnd = map (\(flds, isnum, ml) ->
+                         map (\val ->
+                                  let delta = ml - length val
+                                      filler = replicate delta ' '
+                                  in if delta > 0
+                                     then if isnum
+                                          then filler ++ val
+                                          else val ++ filler
+                                     else val
+                             ) flds
+                    ) (zip3 vtrans numpos mlens)
+   in transpose expnd