diff --git a/Makefile.am b/Makefile.am
index bfb58624d82afb313e5f553cc56f68edb782b490..ff7e00fb1e8e89403ecaf18e59a4b58f03b84bb2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -315,6 +315,7 @@ lib/_autoconf.py: Makefile stamp-directories
 	  echo "KVM_PATH = '$(KVM_PATH)'"; \
 	  echo "KVM_MIGRATION_PORT = '$(KVM_MIGRATION_PORT)'"; \
 	  echo "SOCAT_PATH = '$(SOCAT_PATH)'"; \
+	  echo "LVM_STRIPECOUNT = $(LVM_STRIPECOUNT)" ; \
 	} > $@
 
 $(REPLACE_VARS_SED): Makefile stamp-directories
diff --git a/NEWS b/NEWS
index 768dc728758de88aa8f403bbb44ff813341c2d12..6709d3fa226d38a03e182369a338da5ffdecc222 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,33 @@
+Version 2.0.1
+  - added -H/-B startup parameters to gnt-instance, which will allow
+    re-adding the start in single-user option (regression from 1.2)
+  - the watcher writes the instance status to a file, to allow
+    monitoring to report the instance status (from the master) based on
+    cached results of the watcher's queries; while this can get stale if
+    the watcher is being locked due to other work on the cluster, this
+    is still an improvement
+  - the watcher now also restarts the node daemon and the rapi daemon if
+    they died
+  - fixed the watcher to handle full and drained queue cases
+  - hooks export more instance data in the environment, which helps if
+    hook scripts need to take action based on the instance's properties
+    (no longer need to query back into ganeti)
+  - instance failovers when the instance is stopped do not check for
+    free RAM, so that failing over a stopped instance is possible in low
+    memory situations
+  - rapi uses queries for tags instead of jobs (for less job traffic),
+    and for cluster tags it won't talk to masterd at all but read them
+    from ssconf
+  - a couple of error handling fixes in RAPI
+  - drbd handling: improved the error handling of inconsistent disks
+    after resync to reduce the frequency of "there are some degraded
+    disks for this instance" message
+  - fixed a bug in live migration when DRBD doesn't want to reconnect
+    (the error handling path called a wrong function name)
+
+Version 2.0.0 final
+  - no changes from rc5
+
 Version 2.0 release candidate 5
   - fix a couple of bugs (validation, argument checks)
   - fix gnt-cluster getmaster on non-master nodes (regression)
diff --git a/configure.ac b/configure.ac
index 03476cfa881e21e37595290bdc8054cf7f680132..f9eddc061afd6ecae7d20feeaa648f3a54bd7dd4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,8 +1,8 @@
 # Configure script for Ganeti
 m4_define([gnt_version_major], [2])
 m4_define([gnt_version_minor], [0])
-m4_define([gnt_version_revision], [0])
-m4_define([gnt_version_suffix], [~rc5])
+m4_define([gnt_version_revision], [1])
+m4_define([gnt_version_suffix], [])
 m4_define([gnt_version_full],
           m4_format([%d.%d.%d%s],
                     gnt_version_major, gnt_version_minor,
@@ -119,6 +119,16 @@ AC_ARG_WITH([socat-path],
   [socat_path="/usr/bin/socat"])
 AC_SUBST(SOCAT_PATH, $socat_path)
 
+# ---with-lvm-stripecount=...
+AC_ARG_WITH([lvm-stripecount],
+  [AS_HELP_STRING([--with-lvm-stripecount=NUM],
+    [the number of stripes to use for LVM volumes]
+    [ (default is 3)]
+  )],
+  [lvm_stripecount="$withval"],
+  [lvm_stripecount="3"])
+AC_SUBST(LVM_STRIPECOUNT, $lvm_stripecount)
+
 # Check common programs
 AC_PROG_INSTALL
 AC_PROG_LN_S
diff --git a/daemons/ganeti-watcher b/daemons/ganeti-watcher
index 42a2eaf832857cb2163a7385fb962051af540ab4..2749de63f77a0a2ae6c99ed0e3b13642a0e9ba5d 100755
--- a/daemons/ganeti-watcher
+++ b/daemons/ganeti-watcher
@@ -80,6 +80,20 @@ def StartMaster():
   return not result.failed
 
 
+def EnsureDaemon(daemon):
+  """Check for and start daemon if not alive.
+
+  """
+  pidfile = utils.DaemonPidFileName(daemon)
+  pid = utils.ReadPidFile(pidfile)
+  if pid == 0 or not utils.IsProcessAlive(pid): # no file or dead pid
+    logging.debug("Daemon '%s' not alive, trying to restart", daemon)
+    result = utils.RunCmd([daemon])
+    if not result:
+      logging.error("Can't start daemon '%s', failure %s, output: %s",
+                    daemon, result.fail_reason, result.output)
+
+
 class WatcherState(object):
   """Interface to a state file recording restart attempts.
 
@@ -255,10 +269,17 @@ def GetClusterData():
 
   all_results = cli.PollJob(job_id, cl=client, feedback_fn=logging.debug)
 
+  logging.debug("Got data from cluster, writing instance status file")
+
   result = all_results[0]
   smap = {}
 
   instances = {}
+
+  # write the upfile
+  up_data = "".join(["%s %s\n" % (fields[0], fields[1]) for fields in result])
+  utils.WriteFile(file_name=constants.INSTANCE_UPFILE, data=up_data)
+
   for fields in result:
     (name, status, autostart, snodes) = fields
 
@@ -291,6 +312,9 @@ class Watcher(object):
     master = client.QueryConfigValues(["master_node"])[0]
     if master != utils.HostInfo().name:
       raise NotMasterError("This is not the master node")
+    # first archive old jobs
+    self.ArchiveJobs(opts.job_age)
+    # and only then submit new ones
     self.instances, self.bootids, self.smap = GetClusterData()
     self.started_instances = set()
     self.opts = opts
@@ -300,12 +324,12 @@ class Watcher(object):
 
     """
     notepad = self.notepad
-    self.ArchiveJobs(self.opts.job_age)
     self.CheckInstances(notepad)
     self.CheckDisks(notepad)
     self.VerifyDisks()
 
-  def ArchiveJobs(self, age):
+  @staticmethod
+  def ArchiveJobs(age):
     """Archive old jobs.
 
     """
@@ -452,8 +476,12 @@ def main():
   utils.SetupLogging(constants.LOG_WATCHER, debug=options.debug,
                      stderr_logging=options.debug)
 
-  update_file = True
+  update_file = False
   try:
+    # on master or not, try to start the node dameon (use _PID but is
+    # the same as daemon name)
+    EnsureDaemon(constants.NODED_PID)
+
     notepad = WatcherState()
     try:
       try:
@@ -461,24 +489,30 @@ def main():
       except errors.OpPrereqError:
         # this is, from cli.GetClient, a not-master case
         logging.debug("Not on master, exiting")
+        update_file = True
         sys.exit(constants.EXIT_SUCCESS)
       except luxi.NoMasterError, err:
         logging.warning("Master seems to be down (%s), trying to restart",
                         str(err))
         if not StartMaster():
           logging.critical("Can't start the master, exiting")
-          update_file = False
           sys.exit(constants.EXIT_FAILURE)
         # else retry the connection
         client = cli.GetClient()
 
+      # we are on master now (use _PID but is the same as daemon name)
+      EnsureDaemon(constants.RAPI_PID)
+
       try:
         watcher = Watcher(options, notepad)
       except errors.ConfigurationError:
         # Just exit if there's no configuration
+        update_file = True
         sys.exit(constants.EXIT_SUCCESS)
 
       watcher.Run()
+      update_file = True
+
     finally:
       if update_file:
         notepad.Save()
@@ -492,6 +526,10 @@ def main():
   except errors.ResolverError, err:
     logging.error("Cannot resolve hostname '%s', exiting.", err.args[0])
     sys.exit(constants.EXIT_NODESETUP_ERROR)
+  except errors.JobQueueFull:
+    logging.error("Job queue is full, can't query cluster state")
+  except errors.JobQueueDrainError:
+    logging.error("Job queue is drained, can't maintain cluster state")
   except Exception, err:
     logging.error(str(err), exc_info=True)
     sys.exit(constants.EXIT_FAILURE)
diff --git a/lib/backend.py b/lib/backend.py
index 7a0f5d19ebcde25a17227a4408439ea52d10c34c..4c1df6038382a3ab3a149948bf3a21690772302e 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -1103,7 +1103,7 @@ def BlockdevCreate(disk, size, owner, on_primary, info):
       clist.append(crdev)
 
   try:
-    device = bdev.Create(disk.dev_type, disk.physical_id, clist, size)
+    device = bdev.Create(disk.dev_type, disk.physical_id, clist, disk.size)
   except errors.BlockDeviceError, err:
     _Fail("Can't create block device: %s", err)
 
@@ -1205,7 +1205,7 @@ def _RecursiveAssembleBD(disk, owner, as_primary):
       children.append(cdev)
 
   if as_primary or disk.AssembleOnSecondary():
-    r_dev = bdev.Assemble(disk.dev_type, disk.physical_id, children)
+    r_dev = bdev.Assemble(disk.dev_type, disk.physical_id, children, disk.size)
     r_dev.SetSyncSpeed(constants.SYNC_SPEED)
     result = r_dev
     if as_primary or disk.OpenOnSecondary():
@@ -1361,7 +1361,7 @@ def _RecursiveFindBD(disk):
     for chdisk in disk.children:
       children.append(_RecursiveFindBD(chdisk))
 
-  return bdev.FindDevice(disk.dev_type, disk.physical_id, children)
+  return bdev.FindDevice(disk.dev_type, disk.physical_id, children, disk.size)
 
 
 def BlockdevFind(disk):
@@ -1683,6 +1683,10 @@ def OSEnvironment(instance, debug=0):
       result['NIC_%d_FRONTEND_TYPE' % idx] = \
         instance.hvparams[constants.HV_NIC_TYPE]
 
+  for source, kind in [(instance.beparams, "BE"), (instance.hvparams, "HV")]:
+    for key, value in source.items():
+      result["INSTANCE_%s_%s" % (kind, key)] = str(value)
+
   return result
 
 def BlockdevGrow(disk, amount):
@@ -2330,7 +2334,7 @@ def DrbdAttachNet(nodes_ip, disks, instance_name, multimaster):
         # standalone, even though this should not happen with the
         # new staged way of changing disk configs
         try:
-          rd.ReAttachNet(multimaster)
+          rd.AttachNet(multimaster)
         except errors.BlockDeviceError, err:
           _Fail("Can't change network configuration: %s", err)
     if all_connected:
diff --git a/lib/bdev.py b/lib/bdev.py
index 9d3f08b096e3c71fa215f1a268313fc483e1b26a..5f94d5078c123f59a93c18b91cf7c9a8bd8eb083 100644
--- a/lib/bdev.py
+++ b/lib/bdev.py
@@ -108,13 +108,14 @@ class BlockDev(object):
   after assembly we'll have our correct major/minor.
 
   """
-  def __init__(self, unique_id, children):
+  def __init__(self, unique_id, children, size):
     self._children = children
     self.dev_path = None
     self.unique_id = unique_id
     self.major = None
     self.minor = None
     self.attached = False
+    self.size = size
 
   def Assemble(self):
     """Assemble the device from its components.
@@ -286,13 +287,13 @@ class LogicalVolume(BlockDev):
   """Logical Volume block device.
 
   """
-  def __init__(self, unique_id, children):
+  def __init__(self, unique_id, children, size):
     """Attaches to a LV device.
 
     The unique_id is a tuple (vg_name, lv_name)
 
     """
-    super(LogicalVolume, self).__init__(unique_id, children)
+    super(LogicalVolume, self).__init__(unique_id, children, size)
     if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
       raise ValueError("Invalid configuration data %s" % str(unique_id))
     self._vg_name, self._lv_name = unique_id
@@ -318,18 +319,27 @@ class LogicalVolume(BlockDev):
 
     pvlist = [ pv[1] for pv in pvs_info ]
     free_size = sum([ pv[0] for pv in pvs_info ])
+    current_pvs = len(pvlist)
+    stripes = min(current_pvs, constants.LVM_STRIPECOUNT)
 
     # The size constraint should have been checked from the master before
     # calling the create function.
     if free_size < size:
       _ThrowError("Not enough free space: required %s,"
                   " available %s", size, free_size)
-    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-n%s" % lv_name,
-                           vg_name] + pvlist)
+    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
+    # If the free space is not well distributed, we won't be able to
+    # create an optimally-striped volume; in that case, we want to try
+    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
+    # stripes
+    for stripes_arg in range(stripes, 0, -1):
+      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
+      if not result.failed:
+        break
     if result.failed:
       _ThrowError("LV create failed (%s): %s",
                   result.fail_reason, result.output)
-    return LogicalVolume(unique_id, children)
+    return LogicalVolume(unique_id, children, size)
 
   @staticmethod
   def GetPVInfo(vg_name):
@@ -500,7 +510,7 @@ class LogicalVolume(BlockDev):
     snap_name = self._lv_name + ".snap"
 
     # remove existing snapshot if found
-    snap = LogicalVolume((self._vg_name, snap_name), None)
+    snap = LogicalVolume((self._vg_name, snap_name), None, size)
     _IgnoreError(snap.Remove)
 
     pvs_info = self.GetPVInfo(self._vg_name)
@@ -568,10 +578,51 @@ class DRBD8Status(object):
   SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
                        "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
 
+  CS_UNCONFIGURED = "Unconfigured"
+  CS_STANDALONE = "StandAlone"
+  CS_WFCONNECTION = "WFConnection"
+  CS_WFREPORTPARAMS = "WFReportParams"
+  CS_CONNECTED = "Connected"
+  CS_STARTINGSYNCS = "StartingSyncS"
+  CS_STARTINGSYNCT = "StartingSyncT"
+  CS_WFBITMAPS = "WFBitMapS"
+  CS_WFBITMAPT = "WFBitMapT"
+  CS_WFSYNCUUID = "WFSyncUUID"
+  CS_SYNCSOURCE = "SyncSource"
+  CS_SYNCTARGET = "SyncTarget"
+  CS_PAUSEDSYNCS = "PausedSyncS"
+  CS_PAUSEDSYNCT = "PausedSyncT"
+  CSET_SYNC = frozenset([
+    CS_WFREPORTPARAMS,
+    CS_STARTINGSYNCS,
+    CS_STARTINGSYNCT,
+    CS_WFBITMAPS,
+    CS_WFBITMAPT,
+    CS_WFSYNCUUID,
+    CS_SYNCSOURCE,
+    CS_SYNCTARGET,
+    CS_PAUSEDSYNCS,
+    CS_PAUSEDSYNCT,
+    ])
+
+  DS_DISKLESS = "Diskless"
+  DS_ATTACHING = "Attaching" # transient state
+  DS_FAILED = "Failed" # transient state, next: diskless
+  DS_NEGOTIATING = "Negotiating" # transient state
+  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
+  DS_OUTDATED = "Outdated"
+  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
+  DS_CONSISTENT = "Consistent"
+  DS_UPTODATE = "UpToDate" # normal state
+
+  RO_PRIMARY = "Primary"
+  RO_SECONDARY = "Secondary"
+  RO_UNKNOWN = "Unknown"
+
   def __init__(self, procline):
     u = self.UNCONF_RE.match(procline)
     if u:
-      self.cstatus = "Unconfigured"
+      self.cstatus = self.CS_UNCONFIGURED
       self.lrole = self.rrole = self.ldisk = self.rdisk = None
     else:
       m = self.LINE_RE.match(procline)
@@ -585,21 +636,21 @@ class DRBD8Status(object):
 
     # end reading of data from the LINE_RE or UNCONF_RE
 
-    self.is_standalone = self.cstatus == "StandAlone"
-    self.is_wfconn = self.cstatus == "WFConnection"
-    self.is_connected = self.cstatus == "Connected"
-    self.is_primary = self.lrole == "Primary"
-    self.is_secondary = self.lrole == "Secondary"
-    self.peer_primary = self.rrole == "Primary"
-    self.peer_secondary = self.rrole == "Secondary"
+    self.is_standalone = self.cstatus == self.CS_STANDALONE
+    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
+    self.is_connected = self.cstatus == self.CS_CONNECTED
+    self.is_primary = self.lrole == self.RO_PRIMARY
+    self.is_secondary = self.lrole == self.RO_SECONDARY
+    self.peer_primary = self.rrole == self.RO_PRIMARY
+    self.peer_secondary = self.rrole == self.RO_SECONDARY
     self.both_primary = self.is_primary and self.peer_primary
     self.both_secondary = self.is_secondary and self.peer_secondary
 
-    self.is_diskless = self.ldisk == "Diskless"
-    self.is_disk_uptodate = self.ldisk == "UpToDate"
+    self.is_diskless = self.ldisk == self.DS_DISKLESS
+    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
 
-    self.is_in_resync = self.cstatus in ("SyncSource", "SyncTarget")
-    self.is_in_use = self.cstatus != "Unconfigured"
+    self.is_in_resync = self.cstatus in self.CSET_SYNC
+    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
 
     m = self.SYNC_RE.match(procline)
     if m:
@@ -609,13 +660,16 @@ class DRBD8Status(object):
       seconds = int(m.group(4))
       self.est_time = hours * 3600 + minutes * 60 + seconds
     else:
-      self.sync_percent = None
+      # we have (in this if branch) no percent information, but if
+      # we're resyncing we need to 'fake' a sync percent information,
+      # as this is how cmdlib determines if it makes sense to wait for
+      # resyncing or not
+      if self.is_in_resync:
+        self.sync_percent = 0
+      else:
+        self.sync_percent = None
       self.est_time = None
 
-    self.is_sync_target = self.peer_sync_source = self.cstatus == "SyncTarget"
-    self.peer_sync_target = self.is_sync_source = self.cstatus == "SyncSource"
-    self.is_resync = self.is_sync_target or self.is_sync_source
-
 
 class BaseDRBD(BlockDev):
   """Base DRBD class.
@@ -805,10 +859,10 @@ class DRBD8(BaseDRBD):
   # timeout constants
   _NET_RECONFIG_TIMEOUT = 60
 
-  def __init__(self, unique_id, children):
+  def __init__(self, unique_id, children, size):
     if children and children.count(None) > 0:
       children = []
-    super(DRBD8, self).__init__(unique_id, children)
+    super(DRBD8, self).__init__(unique_id, children, size)
     self.major = self._DRBD_MAJOR
     version = self._GetVersion()
     if version['k_major'] != 8 :
@@ -1031,12 +1085,15 @@ class DRBD8(BaseDRBD):
     return retval
 
   @classmethod
-  def _AssembleLocal(cls, minor, backend, meta):
+  def _AssembleLocal(cls, minor, backend, meta, size):
     """Configure the local part of a DRBD device.
 
     """
     args = ["drbdsetup", cls._DevPath(minor), "disk",
-            backend, meta, "0", "-e", "detach", "--create-device"]
+            backend, meta, "0",
+            "-d", "%sm" % size,
+            "-e", "detach",
+            "--create-device"]
     result = utils.RunCmd(args)
     if result.failed:
       _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
@@ -1113,7 +1170,7 @@ class DRBD8(BaseDRBD):
     self._CheckMetaSize(meta.dev_path)
     self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
 
-    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path)
+    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
     self._children = devices
 
   def RemoveChildren(self, devices):
@@ -1396,7 +1453,7 @@ class DRBD8(BaseDRBD):
       if match_r and "local_dev" not in info:
         # no local disk, but network attached and it matches
         self._AssembleLocal(minor, self._children[0].dev_path,
-                            self._children[1].dev_path)
+                            self._children[1].dev_path, self.size)
         if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
           break
         else:
@@ -1447,7 +1504,7 @@ class DRBD8(BaseDRBD):
     minor = self._aminor
     if self._children and self._children[0] and self._children[1]:
       self._AssembleLocal(minor, self._children[0].dev_path,
-                          self._children[1].dev_path)
+                          self._children[1].dev_path, self.size)
     if self._lhost and self._lport and self._rhost and self._rport:
       self._AssembleNet(minor,
                         (self._lhost, self._lport, self._rhost, self._rport),
@@ -1535,7 +1592,7 @@ class DRBD8(BaseDRBD):
                   aminor, meta)
     cls._CheckMetaSize(meta.dev_path)
     cls._InitMeta(aminor, meta.dev_path)
-    return cls(unique_id, children)
+    return cls(unique_id, children, size)
 
   def Grow(self, amount):
     """Resize the DRBD device and its backing storage.
@@ -1559,13 +1616,13 @@ class FileStorage(BlockDev):
   The unique_id for the file device is a (file_driver, file_path) tuple.
 
   """
-  def __init__(self, unique_id, children):
+  def __init__(self, unique_id, children, size):
     """Initalizes a file device backend.
 
     """
     if children:
       raise errors.BlockDeviceError("Invalid setup for file device")
-    super(FileStorage, self).__init__(unique_id, children)
+    super(FileStorage, self).__init__(unique_id, children, size)
     if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
       raise ValueError("Invalid configuration data %s" % str(unique_id))
     self.driver = unique_id[0]
@@ -1653,7 +1710,7 @@ class FileStorage(BlockDev):
     except IOError, err:
       _ThrowError("Error in file creation: %", str(err))
 
-    return FileStorage(unique_id, children)
+    return FileStorage(unique_id, children, size)
 
 
 DEV_MAP = {
@@ -1663,7 +1720,7 @@ DEV_MAP = {
   }
 
 
-def FindDevice(dev_type, unique_id, children):
+def FindDevice(dev_type, unique_id, children, size):
   """Search for an existing, assembled device.
 
   This will succeed only if the device exists and is assembled, but it
@@ -1672,13 +1729,13 @@ def FindDevice(dev_type, unique_id, children):
   """
   if dev_type not in DEV_MAP:
     raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
-  device = DEV_MAP[dev_type](unique_id, children)
+  device = DEV_MAP[dev_type](unique_id, children, size)
   if not device.attached:
     return None
   return device
 
 
-def Assemble(dev_type, unique_id, children):
+def Assemble(dev_type, unique_id, children, size):
   """Try to attach or assemble an existing device.
 
   This will attach to assemble the device, as needed, to bring it
@@ -1687,7 +1744,7 @@ def Assemble(dev_type, unique_id, children):
   """
   if dev_type not in DEV_MAP:
     raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
-  device = DEV_MAP[dev_type](unique_id, children)
+  device = DEV_MAP[dev_type](unique_id, children, size)
   device.Assemble()
   return device
 
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 24f931d38ffde994d4de095e673a30bcc30ff6da..a90d201a35b9aba2a7e7cf5b1da86050d49ee02e 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -453,7 +453,8 @@ def _CheckNodeNotDrained(lu, node):
 
 
 def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
-                          memory, vcpus, nics, disk_template, disks):
+                          memory, vcpus, nics, disk_template, disks,
+                          bep, hvp, hypervisor):
   """Builds instance related env variables for hooks
 
   This builds the hook environment from individual variables.
@@ -479,6 +480,12 @@ def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
   @param disk_template: the distk template of the instance
   @type disks: list
   @param disks: the list of (size, mode) pairs
+  @type bep: dict
+  @param bep: the backend parameters for the instance
+  @type hvp: dict
+  @param hvp: the hypervisor parameters for the instance
+  @type hypervisor: string
+  @param hypervisor: the hypervisor for the instance
   @rtype: dict
   @return: the hook environment for this instance
 
@@ -497,6 +504,7 @@ def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
     "INSTANCE_MEMORY": memory,
     "INSTANCE_VCPUS": vcpus,
     "INSTANCE_DISK_TEMPLATE": disk_template,
+    "INSTANCE_HYPERVISOR": hypervisor,
   }
 
   if nics:
@@ -525,6 +533,10 @@ def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
 
   env["INSTANCE_DISK_COUNT"] = disk_count
 
+  for source, kind in [(bep, "BE"), (hvp, "HV")]:
+    for key, value in source.items():
+      env["INSTANCE_%s_%s" % (kind, key)] = value
+
   return env
 
 def _PreBuildNICHooksList(lu, nics):
@@ -564,7 +576,9 @@ def _BuildInstanceHookEnvByObject(lu, instance, override=None):
   @return: the hook environment dictionary
 
   """
-  bep = lu.cfg.GetClusterInfo().FillBE(instance)
+  cluster = lu.cfg.GetClusterInfo()
+  bep = cluster.FillBE(instance)
+  hvp = cluster.FillHV(instance)
   args = {
     'name': instance.name,
     'primary_node': instance.primary_node,
@@ -576,6 +590,9 @@ def _BuildInstanceHookEnvByObject(lu, instance, override=None):
     'nics': _PreBuildNICHooksList(lu, instance.nics),
     'disk_template': instance.disk_template,
     'disks': [(disk.size, disk.mode) for disk in instance.disks],
+    'bep': bep,
+    'hvp': hvp,
+    'hypervisor': instance.hypervisor,
   }
   if override:
     args.update(override)
@@ -1681,6 +1698,7 @@ def _WaitForSync(lu, instance, oneshot=False, unlock=False):
     lu.cfg.SetDiskID(dev, node)
 
   retries = 0
+  degr_retries = 10 # in seconds, as we sleep 1 second each time
   while True:
     max_time = 0
     done = True
@@ -1714,6 +1732,16 @@ def _WaitForSync(lu, instance, oneshot=False, unlock=False):
           rem_time = "no time estimate"
         lu.proc.LogInfo("- device %s: %5.2f%% done, %s" %
                         (instance.disks[i].iv_name, perc_done, rem_time))
+
+    # if we're done but degraded, let's do a few small retries, to
+    # make sure we see a stable and not transient situation; therefore
+    # we force restart of the loop
+    if (done or oneshot) and cumul_degraded and degr_retries > 0:
+      logging.info("Degraded disks found, %d retries left", degr_retries)
+      degr_retries -= 1
+      time.sleep(1)
+      continue
+
     if done or oneshot:
       break
 
@@ -3614,10 +3642,15 @@ class LUFailoverInstance(LogicalUnit):
     target_node = secondary_nodes[0]
     _CheckNodeOnline(self, target_node)
     _CheckNodeNotDrained(self, target_node)
-    # check memory requirements on the secondary node
-    _CheckNodeFreeMemory(self, target_node, "failing over instance %s" %
-                         instance.name, bep[constants.BE_MEMORY],
-                         instance.hypervisor)
+    if instance.admin_up:
+      # check memory requirements on the secondary node
+      _CheckNodeFreeMemory(self, target_node, "failing over instance %s" %
+                           instance.name, bep[constants.BE_MEMORY],
+                           instance.hypervisor)
+    else:
+      self.LogInfo("Not checking memory on the secondary node as"
+                   " instance will not be started")
+
     # check bridge existance
     _CheckInstanceBridgesExist(self, instance, node=target_node)
 
@@ -4399,6 +4432,7 @@ class LUCreateInstance(LogicalUnit):
                                   self.op.hvparams)
     hv_type = hypervisor.GetHypervisor(self.op.hypervisor)
     hv_type.CheckParameterSyntax(filled_hvp)
+    self.hv_full = filled_hvp
 
     # fill and remember the beparams dict
     utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
@@ -4606,6 +4640,9 @@ class LUCreateInstance(LogicalUnit):
       nics=_PreBuildNICHooksList(self, self.nics),
       disk_template=self.op.disk_template,
       disks=[(d["size"], d["mode"]) for d in self.disks],
+      bep=self.be_full,
+      hvp=self.hv_full,
+      hypervisor=self.op.hypervisor,
     ))
 
     nl = ([self.cfg.GetMasterNode(), self.op.pnode] +
@@ -5422,7 +5459,8 @@ class LUReplaceDisks(LogicalUnit):
                     new_net_id)
       new_drbd = objects.Disk(dev_type=constants.LD_DRBD8,
                               logical_id=new_alone_id,
-                              children=dev.children)
+                              children=dev.children,
+                              size=dev.size)
       try:
         _CreateSingleBlockDev(self, new_node, instance, new_drbd,
                               _GetInstanceInfoText(instance), False)
@@ -6397,13 +6435,13 @@ class LUExportInstance(LogicalUnit):
       self.cfg.SetDiskID(disk, src_node)
 
     try:
-      for disk in instance.disks:
+      for idx, disk in enumerate(instance.disks):
         # result.payload will be a snapshot of an lvm leaf of the one we passed
         result = self.rpc.call_blockdev_snapshot(src_node, disk)
         msg = result.fail_msg
         if msg:
-          self.LogWarning("Could not snapshot block device %s on node %s: %s",
-                          disk.logical_id[1], src_node, msg)
+          self.LogWarning("Could not snapshot disk/%s on node %s: %s",
+                          idx, src_node, msg)
           snap_disks.append(False)
         else:
           disk_id = (vgname, result.payload)
@@ -6429,13 +6467,12 @@ class LUExportInstance(LogicalUnit):
                                                instance, cluster_name, idx)
         msg = result.fail_msg
         if msg:
-          self.LogWarning("Could not export block device %s from node %s to"
-                          " node %s: %s", dev.logical_id[1], src_node,
-                          dst_node.name, msg)
+          self.LogWarning("Could not export disk/%s from node %s to"
+                          " node %s: %s", idx, src_node, dst_node.name, msg)
         msg = self.rpc.call_blockdev_remove(src_node, dev).fail_msg
         if msg:
-          self.LogWarning("Could not remove snapshot block device %s from node"
-                          " %s: %s", dev.logical_id[1], src_node, msg)
+          self.LogWarning("Could not remove snapshot for disk/%d from node"
+                          " %s: %s", idx, src_node, msg)
 
     result = self.rpc.call_finalize_export(dst_node.name, instance, snap_disks)
     msg = result.fail_msg
diff --git a/lib/constants.py b/lib/constants.py
index 48626f2f7f47c3dd08bee991a261b473ac1f63b7..01034051e75ad64f9883b0308a70923c0e9af2d0 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -96,6 +96,7 @@ CLUSTER_CONF_FILE = DATA_DIR + "/config.data"
 SSL_CERT_FILE = DATA_DIR + "/server.pem"
 RAPI_CERT_FILE = DATA_DIR + "/rapi.pem"
 WATCHER_STATEFILE = DATA_DIR + "/watcher.data"
+INSTANCE_UPFILE = RUN_GANETI_DIR + "/instance-status"
 SSH_KNOWN_HOSTS_FILE = DATA_DIR + "/known_hosts"
 RAPI_USERS_FILE = DATA_DIR + "/rapi_users"
 QUEUE_DIR = DATA_DIR + "/queue"
@@ -245,6 +246,7 @@ DEFAULT_VG = "xenvg"
 BIND_ADDRESS_GLOBAL = "0.0.0.0"
 MIN_VG_SIZE = 20480
 DEFAULT_MAC_PREFIX = "aa:00:00"
+LVM_STRIPECOUNT = _autoconf.LVM_STRIPECOUNT
 
 # RPC constants
 (RPC_ENCODING_NONE,
diff --git a/lib/rapi/baserlib.py b/lib/rapi/baserlib.py
index 0138e64fde7067fa12fd93555766ad9ab2719e03..2bca63ba07a99ff9ab05fc86718ba16d9d0011a8 100644
--- a/lib/rapi/baserlib.py
+++ b/lib/rapi/baserlib.py
@@ -23,12 +23,17 @@
 
 """
 
+import logging
+
 import ganeti.cli
-import ganeti.opcodes
 
 from ganeti import luxi
 from ganeti import rapi
 from ganeti import http
+from ganeti import ssconf
+from ganeti import constants
+from ganeti import opcodes
+from ganeti import errors
 
 
 def BuildUriList(ids, uri_format, uri_fields=("name", "uri")):
@@ -81,8 +86,22 @@ def _Tags_GET(kind, name=""):
   """Helper function to retrieve tags.
 
   """
-  op = ganeti.opcodes.OpGetTags(kind=kind, name=name)
-  tags = ganeti.cli.SubmitOpCode(op)
+  if kind == constants.TAG_INSTANCE or kind == constants.TAG_NODE:
+    if not name:
+      raise http.HttpBadRequest("Missing name on tag request")
+    cl = GetClient()
+    if kind == constants.TAG_INSTANCE:
+      fn = cl.QueryInstances
+    else:
+      fn = cl.QueryNodes
+    result = fn(names=[name], fields=["tags"], use_locking=False)
+    if not result or not result[0]:
+      raise http.HttpBadGateway("Invalid response from tag query")
+    tags = result[0][0]
+  elif kind == constants.TAG_CLUSTER:
+    ssc = ssconf.SimpleStore()
+    tags = ssc.GetClusterTags()
+
   return list(tags)
 
 
@@ -90,18 +109,14 @@ def _Tags_PUT(kind, tags, name=""):
   """Helper function to set tags.
 
   """
-  cl = luxi.Client()
-  return cl.SubmitJob([ganeti.opcodes.OpAddTags(kind=kind, name=name,
-                                                tags=tags)])
+  return SubmitJob([opcodes.OpAddTags(kind=kind, name=name, tags=tags)])
 
 
 def _Tags_DELETE(kind, tags, name=""):
   """Helper function to delete tags.
 
   """
-  cl = luxi.Client()
-  return cl.SubmitJob([ganeti.opcodes.OpDelTags(kind=kind, name=name,
-                                                tags=tags)])
+  return SubmitJob([opcodes.OpDelTags(kind=kind, name=name, tags=tags)])
 
 
 def MapBulkFields(itemslist, fields):
@@ -147,6 +162,51 @@ def MakeParamsDict(opts, params):
   return result
 
 
+def SubmitJob(op, cl=None):
+  """Generic wrapper for submit job, for better http compatibility.
+
+  @type op: list
+  @param op: the list of opcodes for the job
+  @type cl: None or luxi.Client
+  @param cl: optional luxi client to use
+  @rtype: string
+  @return: the job ID
+
+  """
+  try:
+    if cl is None:
+      cl = GetClient()
+    return cl.SubmitJob(op)
+  except errors.JobQueueFull:
+    raise http.HttpServiceUnavailable("Job queue is full, needs archiving")
+  except errors.JobQueueDrainError:
+    raise http.HttpServiceUnavailable("Job queue is drained, cannot submit")
+  except luxi.NoMasterError, err:
+    raise http.HttpBadGateway("Master seems to unreachable: %s" % str(err))
+  except luxi.TimeoutError, err:
+    raise http.HttpGatewayTimeout("Timeout while talking to the master"
+                                  " daemon. Error: %s" % str(err))
+
+def GetClient():
+  """Geric wrapper for luxi.Client(), for better http compatiblity.
+
+  """
+  try:
+    return luxi.Client()
+  except luxi.NoMasterError, err:
+    raise http.HttpBadGateway("Master seems to unreachable: %s" % str(err))
+
+
+def FeedbackFn(ts, log_type, log_msg):
+  """Feedback logging function for http case.
+
+  We don't have a stdout for printing log messages, so log them to the
+  http log at least.
+
+  """
+  logging.info("%s: %s", log_type, log_msg)
+
+
 class R_Generic(object):
   """Generic class for resources.
 
diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py
index ef35d388e693462a5edc00ec4e5a4b8c27aa0425..c4b2d3ec898c06925c7de0423c6fbf88d6233bb7 100644
--- a/lib/rapi/rlib2.py
+++ b/lib/rapi/rlib2.py
@@ -23,13 +23,14 @@
 
 """
 
-import ganeti.opcodes
+from ganeti import opcodes
 from ganeti import http
-from ganeti import luxi
 from ganeti import constants
+from ganeti import cli
 from ganeti.rapi import baserlib
 
 
+
 I_FIELDS = ["name", "admin_state", "os",
             "pnode", "snodes",
             "disk_template",
@@ -70,7 +71,7 @@ class R_2_info(baserlib.R_Generic):
     """Returns cluster information.
 
     """
-    client = luxi.Client()
+    client = baserlib.GetClient()
     return client.QueryClusterInfo()
 
 
@@ -86,12 +87,15 @@ class R_2_os(baserlib.R_Generic):
     Example: ["debian-etch"]
 
     """
-    op = ganeti.opcodes.OpDiagnoseOS(output_fields=["name", "valid"],
-                                     names=[])
-    diagnose_data = ganeti.cli.SubmitOpCode(op)
+    cl = baserlib.GetClient()
+    op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
+    job_id = baserlib.SubmitJob([op], cl)
+    # we use custom feedback function, instead of print we log the status
+    result = cli.PollJob(job_id, cl, feedback_fn=baserlib.FeedbackFn)
+    diagnose_data = result[0]
 
     if not isinstance(diagnose_data, list):
-      raise http.HttpInternalServerError(message="Can't get OS list")
+      raise http.HttpBadGateway(message="Can't get OS list")
 
     return [row[0] for row in diagnose_data if row[1]]
 
@@ -107,8 +111,9 @@ class R_2_jobs(baserlib.R_Generic):
 
     """
     fields = ["id"]
+    cl = baserlib.GetClient()
     # Convert the list of lists to the list of ids
-    result = [job_id for [job_id] in luxi.Client().QueryJobs(None, fields)]
+    result = [job_id for [job_id] in cl.QueryJobs(None, fields)]
     return baserlib.BuildUriList(result, "/2/jobs/%s",
                                  uri_fields=("id", "uri"))
 
@@ -135,7 +140,7 @@ class R_2_jobs_id(baserlib.R_Generic):
               "received_ts", "start_ts", "end_ts",
               ]
     job_id = self.items[0]
-    result = luxi.Client().QueryJobs([job_id, ], fields)[0]
+    result = baserlib.GetClient().QueryJobs([job_id, ], fields)[0]
     if result is None:
       raise http.HttpNotFound()
     return baserlib.MapFields(fields, result)
@@ -145,7 +150,7 @@ class R_2_jobs_id(baserlib.R_Generic):
 
     """
     job_id = self.items[0]
-    result = luxi.Client().CancelJob(job_id)
+    result = baserlib.GetClient().CancelJob(job_id)
     return result
 
 
@@ -157,7 +162,7 @@ class R_2_nodes(baserlib.R_Generic):
     """Returns a list of all nodes.
 
     """
-    client = luxi.Client()
+    client = baserlib.GetClient()
 
     if self.useBulk():
       bulkdata = client.QueryNodes([], N_FIELDS, False)
@@ -178,7 +183,7 @@ class R_2_nodes_name(baserlib.R_Generic):
 
     """
     node_name = self.items[0]
-    client = luxi.Client()
+    client = baserlib.GetClient()
     result = client.QueryNodes(names=[node_name], fields=N_FIELDS,
                                use_locking=self.useLocking())
 
@@ -193,7 +198,7 @@ class R_2_instances(baserlib.R_Generic):
     """Returns a list of all available instances.
 
     """
-    client = luxi.Client()
+    client = baserlib.GetClient()
 
     use_locking = self.useLocking()
     if self.useBulk():
@@ -235,28 +240,27 @@ class R_2_instances(baserlib.R_Generic):
              "ip": fn("ip", None),
              "bridge": fn("bridge", None)}]
 
-    op = ganeti.opcodes.OpCreateInstance(
-        mode=constants.INSTANCE_CREATE,
-        instance_name=fn('name'),
-        disks=disks,
-        disk_template=fn('disk_template'),
-        os_type=fn('os'),
-        pnode=fn('pnode', None),
-        snode=fn('snode', None),
-        iallocator=fn('iallocator', None),
-        nics=nics,
-        start=fn('start', True),
-        ip_check=fn('ip_check', True),
-        wait_for_sync=True,
-        hypervisor=fn('hypervisor', None),
-        hvparams=hvparams,
-        beparams=beparams,
-        file_storage_dir=fn('file_storage_dir', None),
-        file_driver=fn('file_driver', 'loop'),
-        )
-
-    job_id = ganeti.cli.SendJob([op])
-    return job_id
+    op = opcodes.OpCreateInstance(
+      mode=constants.INSTANCE_CREATE,
+      instance_name=fn('name'),
+      disks=disks,
+      disk_template=fn('disk_template'),
+      os_type=fn('os'),
+      pnode=fn('pnode', None),
+      snode=fn('snode', None),
+      iallocator=fn('iallocator', None),
+      nics=nics,
+      start=fn('start', True),
+      ip_check=fn('ip_check', True),
+      wait_for_sync=True,
+      hypervisor=fn('hypervisor', None),
+      hvparams=hvparams,
+      beparams=beparams,
+      file_storage_dir=fn('file_storage_dir', None),
+      file_driver=fn('file_driver', 'loop'),
+      )
+
+    return baserlib.SubmitJob([op])
 
 
 class R_2_instances_name(baserlib.R_Generic):
@@ -267,7 +271,7 @@ class R_2_instances_name(baserlib.R_Generic):
     """Send information about an instance.
 
     """
-    client = luxi.Client()
+    client = baserlib.GetClient()
     instance_name = self.items[0]
     result = client.QueryInstances(names=[instance_name], fields=I_FIELDS,
                                    use_locking=self.useLocking())
@@ -278,10 +282,9 @@ class R_2_instances_name(baserlib.R_Generic):
     """Delete an instance.
 
     """
-    op = ganeti.opcodes.OpRemoveInstance(instance_name=self.items[0],
-                                         ignore_failures=False)
-    job_id = ganeti.cli.SendJob([op])
-    return job_id
+    op = opcodes.OpRemoveInstance(instance_name=self.items[0],
+                                  ignore_failures=False)
+    return baserlib.SubmitJob([op])
 
 
 class R_2_instances_name_reboot(baserlib.R_Generic):
@@ -302,14 +305,11 @@ class R_2_instances_name_reboot(baserlib.R_Generic):
                                      [constants.INSTANCE_REBOOT_HARD])[0]
     ignore_secondaries = bool(self.queryargs.get('ignore_secondaries',
                                                  [False])[0])
-    op = ganeti.opcodes.OpRebootInstance(
-        instance_name=instance_name,
-        reboot_type=reboot_type,
-        ignore_secondaries=ignore_secondaries)
-
-    job_id = ganeti.cli.SendJob([op])
+    op = opcodes.OpRebootInstance(instance_name=instance_name,
+                                  reboot_type=reboot_type,
+                                  ignore_secondaries=ignore_secondaries)
 
-    return job_id
+    return baserlib.SubmitJob([op])
 
 
 class R_2_instances_name_startup(baserlib.R_Generic):
@@ -327,12 +327,10 @@ class R_2_instances_name_startup(baserlib.R_Generic):
     """
     instance_name = self.items[0]
     force_startup = bool(self.queryargs.get('force', [False])[0])
-    op = ganeti.opcodes.OpStartupInstance(instance_name=instance_name,
-                                          force=force_startup)
+    op = opcodes.OpStartupInstance(instance_name=instance_name,
+                                   force=force_startup)
 
-    job_id = ganeti.cli.SendJob([op])
-
-    return job_id
+    return baserlib.SubmitJob([op])
 
 
 class R_2_instances_name_shutdown(baserlib.R_Generic):
@@ -346,11 +344,9 @@ class R_2_instances_name_shutdown(baserlib.R_Generic):
 
     """
     instance_name = self.items[0]
-    op = ganeti.opcodes.OpShutdownInstance(instance_name=instance_name)
-
-    job_id = ganeti.cli.SendJob([op])
+    op = opcodes.OpShutdownInstance(instance_name=instance_name)
 
-    return job_id
+    return baserlib.SubmitJob([op])
 
 
 class _R_Tags(baserlib.R_Generic):
diff --git a/lib/rpc.py b/lib/rpc.py
index b5d0c53c84e6c95b6d2f1aefadbf15f2c097bb67..220232c0ace16e00ecef131d5399d433a0d341d2 100644
--- a/lib/rpc.py
+++ b/lib/rpc.py
@@ -948,7 +948,10 @@ class RpcRunner(object):
     """
     flat_disks = []
     for disk in snap_disks:
-      flat_disks.append(disk.ToDict())
+      if isinstance(disk, bool):
+        flat_disks.append(disk)
+      else:
+        flat_disks.append(disk.ToDict())
 
     return self._SingleNodeCall(node, "finalize_export",
                                 [self._InstDict(instance), flat_disks])
diff --git a/lib/ssconf.py b/lib/ssconf.py
index cce1141f073d4e807d49a24a6cbb5ab275b86dad..19a95c97a90d0165a14eb102ee756e58f17ce44a 100644
--- a/lib/ssconf.py
+++ b/lib/ssconf.py
@@ -250,6 +250,14 @@ class SimpleStore(object):
     nl = data.splitlines(False)
     return nl
 
+  def GetClusterTags(self):
+    """Return the cluster tags.
+
+    """
+    data = self._ReadFile(constants.SS_CLUSTER_TAGS)
+    nl = data.splitlines(False)
+    return nl
+
 
 def GetMasterAndMyself(ss=None):
   """Get the master node and my own hostname.
diff --git a/lib/utils.py b/lib/utils.py
index 89436d8bbb76940f6ece1fd0392d48c091d9a144..2ec0b312bc1b52568369e511034929e1fd632a85 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -1797,11 +1797,13 @@ def SafeEncode(text):
   """Return a 'safe' version of a source string.
 
   This function mangles the input string and returns a version that
-  should be safe to disply/encode as ASCII. To this end, we first
+  should be safe to display/encode as ASCII. To this end, we first
   convert it to ASCII using the 'backslashreplace' encoding which
-  should get rid of any non-ASCII chars, and then we again encode it
-  via 'string_escape' which converts '\n' into '\\n' so that log
-  messages remain one-line.
+  should get rid of any non-ASCII chars, and then we process it
+  through a loop copied from the string repr sources in the python; we
+  don't use string_escape anymore since that escape single quotes and
+  backslashes too, and that is too much; and that escaping is not
+  stable, i.e. string_escape(string_escape(x)) != string_escape(x).
 
   @type text: str or unicode
   @param text: input data
@@ -1809,9 +1811,23 @@ def SafeEncode(text):
   @return: a safe version of text
 
   """
-  text = text.encode('ascii', 'backslashreplace')
-  text = text.encode('string_escape')
-  return text
+  if isinstance(text, unicode):
+    # onli if unicode; if str already, we handle it below
+    text = text.encode('ascii', 'backslashreplace')
+  resu = ""
+  for char in text:
+    c = ord(char)
+    if char  == '\t':
+      resu += r'\t'
+    elif char == '\n':
+      resu += r'\n'
+    elif char == '\r':
+      resu += r'\'r'
+    elif c < 32 or c >= 127: # non-printable
+      resu += "\\x%02x" % (c & 0xff)
+    else:
+      resu += char
+  return resu
 
 
 def CommaJoin(names):
diff --git a/man/gnt-backup.sgml b/man/gnt-backup.sgml
index f7efd78362d0c5c67f6db4653ba05d81b20080af..d432b6ef6eda9190005f9752ce3df65579ea7c5c 100644
--- a/man/gnt-backup.sgml
+++ b/man/gnt-backup.sgml
@@ -168,7 +168,7 @@
 
       <para>
         The NICs of the instances can be specified via the
-        <option>--nic</option> option. By default, one NIC is created
+        <option>--net</option> option. By default, one NIC is created
         for the instance, with the MAC set to the original MAC of the
         instance (as it was at export time). Each NIC can take up to
         three parameters (all optional):
diff --git a/man/gnt-cluster.sgml b/man/gnt-cluster.sgml
index 6c8f4e745582d79a1af82528d9989f031d410799..13bdf03a3365402032f376ee997df2da7bae29f5 100644
--- a/man/gnt-cluster.sgml
+++ b/man/gnt-cluster.sgml
@@ -627,7 +627,7 @@
         Example:
       </para>
 <screen>
-# gnt-cluster search time
+# gnt-cluster search-tags time
 /cluster ctime:2007-09-01
 /nodes/node1.example.com mtime:2007-10-04
 </screen>
diff --git a/man/gnt-instance.sgml b/man/gnt-instance.sgml
index 2d9d44fb03b57c3223ddfb7557359bb9c641a2f7..c4dd54df32d8ae4ff199bdfbbb9f7195fb858ebf 100644
--- a/man/gnt-instance.sgml
+++ b/man/gnt-instance.sgml
@@ -148,7 +148,7 @@
 
         <para>
           The NICs of the instances can be specified via the
-          <option>--nic</option> option. By default, one NIC is
+          <option>--net</option> option. By default, one NIC is
           created for the instance, with a random MAC, and connected
           to the default bridge. Each NIC can take up to three
           parameters (all optional):
@@ -1200,12 +1200,12 @@ instance5: 11225
         </para>
 
         <para>
-          The <option>--nic
+          The <option>--net
           add:<replaceable>options</replaceable></option> option will
           add a new NIC to the instance. The available options are the
           same as in the <command>add</command> command (mac, ip,
-          bridge). The <option>--nice remove</option> will remove the
-          last NIC of the instance, while the <option>--nic
+          bridge). The <option>--net remove</option> will remove the
+          last NIC of the instance, while the <option>--net
           <replaceable>N</replaceable>:<replaceable>options</replaceable></option>
           option will change the parameters of the Nth instance NIC.
         </para>
diff --git a/test/ganeti.constants_unittest.py b/test/ganeti.constants_unittest.py
index 7652f869b8108f1cfe980376238e58b0fc3e5d76..e5263d43c2d340b4bbd94325784dad206f591cd3 100755
--- a/test/ganeti.constants_unittest.py
+++ b/test/ganeti.constants_unittest.py
@@ -23,6 +23,7 @@
 
 
 import unittest
+import re
 
 from ganeti import constants
 
@@ -54,5 +55,17 @@ class TestConstants(unittest.TestCase):
                      constants.CONFIG_REVISION))
 
 
+class TestParameterNames(unittest.TestCase):
+  """HV/BE parameter tests"""
+  VALID_NAME = re.compile("^[a-zA-Z_][a-zA-Z0-9_]*$")
+
+  def testNoDashes(self):
+    for kind, source in [('hypervisor', constants.HVS_PARAMETER_TYPES),
+                         ('backend', constants.BES_PARAMETER_TYPES)]:
+      for key in source:
+        self.failUnless(self.VALID_NAME.match(key),
+                        "The %s parameter '%s' contains invalid characters" %
+                        (kind, key))
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py
index ddb47000da16c8d0f2a75018f0206503b27e1ef7..415ffa7d6b3e47cc2ec6fba343a556b4221c19f4 100755
--- a/test/ganeti.utils_unittest.py
+++ b/test/ganeti.utils_unittest.py
@@ -33,6 +33,7 @@ import socket
 import shutil
 import re
 import select
+import string
 
 import ganeti
 import testutils
@@ -44,7 +45,7 @@ from ganeti.utils import IsProcessAlive, RunCmd, \
      ParseUnit, AddAuthorizedKey, RemoveAuthorizedKey, \
      ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
      SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree, OwnIpAddress, \
-     TailFile, ForceDictType, IsNormAbsPath
+     TailFile, ForceDictType, SafeEncode, IsNormAbsPath
 
 from ganeti.errors import LockError, UnitParseError, GenericError, \
      ProgrammerError
@@ -966,5 +967,25 @@ class TestIsAbsNormPath(unittest.TestCase):
     self._pathTestHelper('/etc/../root', False)
     self._pathTestHelper('/etc/', False)
 
+
+class TestSafeEncode(unittest.TestCase):
+  """Test case for SafeEncode"""
+
+  def testAscii(self):
+    for txt in [string.digits, string.letters, string.punctuation]:
+      self.failUnlessEqual(txt, SafeEncode(txt))
+
+  def testDoubleEncode(self):
+    for i in range(255):
+      txt = SafeEncode(chr(i))
+      self.failUnlessEqual(txt, SafeEncode(txt))
+
+  def testUnicode(self):
+    # 1024 is high enough to catch non-direct ASCII mappings
+    for i in range(1024):
+      txt = SafeEncode(unichr(i))
+      self.failUnlessEqual(txt, SafeEncode(txt))
+
+
 if __name__ == '__main__':
   unittest.main()