diff --git a/src/Ganeti/HTools/CLI.hs b/src/Ganeti/HTools/CLI.hs
index 5e9bf084006cb992172053437b96bc49548e5711..8f55f6fb40489d2f64e5427bf5d7d80c697a3e4e 100644
--- a/src/Ganeti/HTools/CLI.hs
+++ b/src/Ganeti/HTools/CLI.hs
@@ -52,6 +52,7 @@ module Ganeti.HTools.CLI
   , oExInst
   , oExTags
   , oExecJobs
+  , oForce
   , oGroup
   , oIAllocSrc
   , oInstMoves
@@ -117,6 +118,7 @@ data Options = Options
   , optExInst      :: [String]       -- ^ Instances to be excluded
   , optExTags      :: Maybe [String] -- ^ Tags to use for exclusion
   , optExecJobs    :: Bool           -- ^ Execute the commands via Luxi
+  , optForce       :: Bool           -- ^ Force the execution
   , optGroup       :: Maybe GroupID  -- ^ The UUID of the group to process
   , optIAllocSrc   :: Maybe FilePath -- ^ The iallocation spec
   , optSelInst     :: [String]       -- ^ Instances to be excluded
@@ -163,6 +165,7 @@ defaultOptions  = Options
   , optExInst      = []
   , optExTags      = Nothing
   , optExecJobs    = False
+  , optForce       = False
   , optGroup       = Nothing
   , optIAllocSrc   = Nothing
   , optSelInst     = []
@@ -319,6 +322,14 @@ oExecJobs =
    \ it for data gathering)",
    OptComplNone)
 
+oForce :: OptType
+oForce =
+  (Option "f" ["force"]
+   (NoArg (\ opts -> Ok opts {optForce = True}))
+   "force the execution of this program, even if warnings would\
+   \ otherwise prevent it",
+   OptComplNone)
+
 oGroup :: OptType
 oGroup =
   (Option "G" ["group"]
diff --git a/src/Ganeti/HTools/Program/Hroller.hs b/src/Ganeti/HTools/Program/Hroller.hs
index e5584900365f2f17475e6aede13894508ca6dd90..72e863287eac6d40784ca78f5f0d6baca45e2852 100644
--- a/src/Ganeti/HTools/Program/Hroller.hs
+++ b/src/Ganeti/HTools/Program/Hroller.hs
@@ -61,6 +61,7 @@ options = do
     , oNoHeaders
     , oSaveCluster
     , oGroup
+    , oForce
     ]
 
 -- | The list of arguments supported by the program.
@@ -112,11 +113,19 @@ main opts args = do
   unless (null args) $ exitErr "This program doesn't take any arguments."
 
   let verbose = optVerbose opts
+      maybeExit = if optForce opts then warn else exitErr
 
   -- Load cluster data. The last two arguments, cluster tags and ipolicy, are
   -- currently not used by this tool.
   ini_cdata@(ClusterData gl fixed_nl ilf _ _) <- loadExternalData opts
 
+  let master_names = map Node.name . filter Node.isMaster . IntMap.elems $
+                     fixed_nl
+  case master_names of
+    [] -> maybeExit "No master node found (maybe not supported by backend)."
+    [ _ ] -> return ()
+    _ -> exitErr $ "Found more than one master node: " ++  show master_names
+
   nlf <- setNodeStatus opts fixed_nl
 
   maybeSaveData (optSaveCluster opts) "original" "before hroller run" ini_cdata
diff --git a/src/Ganeti/Utils.hs b/src/Ganeti/Utils.hs
index b4a8c3cacfbadee13e0c265399ccfb8e1903b5a9..89a054d9e602d432239d9e0adef650cb06cf57df 100644
--- a/src/Ganeti/Utils.hs
+++ b/src/Ganeti/Utils.hs
@@ -49,6 +49,7 @@ module Ganeti.Utils
   , getCurrentTimeUSec
   , clockTimeToString
   , chompPrefix
+  , warn
   , wrap
   , trim
   , defaultHead
@@ -247,6 +248,10 @@ exitWhen False _  = return ()
 exitUnless :: Bool -> String -> IO ()
 exitUnless cond = exitWhen (not cond)
 
+-- | Print a warning, but do not exit.
+warn :: String -> IO ()
+warn = hPutStrLn stderr . (++) "Warning: "
+
 -- | Helper for 'niceSort'. Computes the key element for a given string.
 extractKey :: [Either Integer String]  -- ^ Current (partial) key, reversed
            -> String                   -- ^ Remaining string
diff --git a/test/hs/shelltests/htools-invalid.test b/test/hs/shelltests/htools-invalid.test
index eabce21bed8ac8e7213b945b61a8cb3a768e69df..c2bbe3e3e9bbb26623c097e5a09946795b2c9dd1 100644
--- a/test/hs/shelltests/htools-invalid.test
+++ b/test/hs/shelltests/htools-invalid.test
@@ -52,10 +52,14 @@ Error: This program doesn't take any arguments.
 Error: This program doesn't take any arguments.
 >>>=1
 
-# hroller fails to build a graph for an empty cluster
+# hroller should notice the absence of a master node
 ./test/hs/hroller -t$TESTDATA_DIR/empty-cluster.data
->>>2
-Error: Cannot create node graph
+>>>2/Error: No master node found/
+>>>=1
+
+# hroller fails to build a graph for an empty cluster
+./test/hs/hroller -f -t$TESTDATA_DIR/empty-cluster.data
+>>>2/Error: Cannot create node graph/
 >>>=1
 
 # hbal doesn't accept invalid priority
diff --git a/test/hs/shelltests/htools-single-group.test b/test/hs/shelltests/htools-single-group.test
index f8e629cb5eb0b9abc587389a0a004d7d9dfaf123..0f271320e5e084178ed28d71e32caebbc1b8f480 100644
--- a/test/hs/shelltests/htools-single-group.test
+++ b/test/hs/shelltests/htools-single-group.test
@@ -28,10 +28,12 @@
 >>> /HCHECK_INIT_CLUSTER_NEED_REBALANCE=0/
 >>>= 0
 
+# FIXME: remove option -f once the text backend supports indicating
+#        the master node
 # hroller should be able to print the solution
-./test/hs/hroller -t$T/simu-onegroup.tiered
+./test/hs/hroller -f -t$T/simu-onegroup.tiered
 >>>= 0
 
 # hroller should be able to print the solution, in verbose mode as well
-./test/hs/hroller -t$T/simu-onegroup.tiered -v -v
+./test/hs/hroller -f -t$T/simu-onegroup.tiered -v -v
 >>>= 0