diff --git a/NEWS b/NEWS
index 6709d3fa226d38a03e182369a338da5ffdecc222..cbbf76a78c842add206e4ed73dc8dd07847c047d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,33 @@
+Version 2.0.2
+  - Added experimental support for stripped logical volumes; this should
+    enhance performance but comes with a higher complexity in the block
+    device handling; stripping is only enabled when passing
+    --with-lvm-stripecount=N to configure, but codepaths are affected
+    even in the non-stripped mode
+  - Improved resiliency against transient failures at the end of DRBD
+    resyncs, and in general of DRBD resync checks
+  - Fixed a couple of issues with exports and snapshot errors
+  - Fixed a couple of issues in instance listing
+  - Added display of the disk size in β€œgnt-instance info”
+  - Fixed checking for valid OSes in instance creation
+  - Fixed handling of the β€˜vcpus’ parameter in instance listing and in
+    general of invalid parameters
+  - Fixed http server library, and thus RAPI, to handle invalid
+    username/password combinations correctly; this means that now they
+    report unauthorized for queries too, not only for modifications,
+    allowing earlier detect of configuration problems
+  - Added a new β€˜role’ node list field, equivalent to the master/master
+    candidate/drained/offline flags combinations
+  - Fixed cluster modify and changes of candidate pool size
+  - Fixed cluster verify error messages for wrong files on regular nodes
+  - Fixed a couple of issues with node demotion from master candidate
+    role
+  - Fixed node readd issues
+  - Added non-interactive mode for β€œganeti-masterd --no-voting” startup
+  - Added a new β€˜--no-voting’ option for masterfailover to fix failover
+    on two-nodes clusters when the former master node is unreachable
+  - Added instance reinstall over RAPI
+
 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)
diff --git a/configure.ac b/configure.ac
index f9eddc061afd6ecae7d20feeaa648f3a54bd7dd4..3b4f0e200ae9cec60ed702c8cda0ac5c7e62f53d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
 # Configure script for Ganeti
 m4_define([gnt_version_major], [2])
 m4_define([gnt_version_minor], [0])
-m4_define([gnt_version_revision], [1])
+m4_define([gnt_version_revision], [2])
 m4_define([gnt_version_suffix], [])
 m4_define([gnt_version_full],
           m4_format([%d.%d.%d%s],
@@ -123,10 +123,10 @@ AC_SUBST(SOCAT_PATH, $socat_path)
 AC_ARG_WITH([lvm-stripecount],
   [AS_HELP_STRING([--with-lvm-stripecount=NUM],
     [the number of stripes to use for LVM volumes]
-    [ (default is 3)]
+    [ (default is 1)]
   )],
   [lvm_stripecount="$withval"],
-  [lvm_stripecount="3"])
+  [lvm_stripecount="1"])
 AC_SUBST(LVM_STRIPECOUNT, $lvm_stripecount)
 
 # Check common programs
diff --git a/lib/bdev.py b/lib/bdev.py
index 4971b53b1e33d1a9164c811f4aae5f62c2d0b0fe..9ac41e50e6db08d3b7d99c5bf0d2ed185f5825cc 100644
--- a/lib/bdev.py
+++ b/lib/bdev.py
@@ -299,7 +299,7 @@ class LogicalVolume(BlockDev):
     self._vg_name, self._lv_name = unique_id
     self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
     self._degraded = True
-    self.major = self.minor = None
+    self.major = self.minor = self.pe_size = self.stripe_count = None
     self.Attach()
 
   @classmethod
@@ -411,19 +411,30 @@ class LogicalVolume(BlockDev):
     """
     self.attached = False
     result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
-                           "-olv_attr,lv_kernel_major,lv_kernel_minor",
-                           self.dev_path])
+                           "--units=m", "--nosuffix",
+                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
+                           "vg_extent_size,stripes", self.dev_path])
     if result.failed:
       logging.error("Can't find LV %s: %s, %s",
                     self.dev_path, result.fail_reason, result.output)
       return False
-    out = result.stdout.strip().rstrip(',')
+    # the output can (and will) have multiple lines for multi-segment
+    # LVs, as the 'stripes' parameter is a segment one, so we take
+    # only the last entry, which is the one we're interested in; note
+    # that with LVM2 anyway the 'stripes' value must be constant
+    # across segments, so this is a no-op actually
+    out = result.stdout.splitlines()
+    if not out: # totally empty result? splitlines() returns at least
+                # one line for any non-empty string
+      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
+      return False
+    out = out[-1].strip().rstrip(',')
     out = out.split(",")
-    if len(out) != 3:
-      logging.error("Can't parse LVS output, len(%s) != 3", str(out))
+    if len(out) != 5:
+      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
       return False
 
-    status, major, minor = out[:3]
+    status, major, minor, pe_size, stripes = out
     if len(status) != 6:
       logging.error("lvs lv_attr is not 6 characters (%s)", status)
       return False
@@ -434,8 +445,22 @@ class LogicalVolume(BlockDev):
     except ValueError, err:
       logging.error("lvs major/minor cannot be parsed: %s", str(err))
 
+    try:
+      pe_size = int(float(pe_size))
+    except (TypeError, ValueError), err:
+      logging.error("Can't parse vg extent size: %s", err)
+      return False
+
+    try:
+      stripes = int(stripes)
+    except (TypeError, ValueError), err:
+      logging.error("Can't parse the number of stripes: %s", err)
+      return False
+
     self.major = major
     self.minor = minor
+    self.pe_size = pe_size
+    self.stripe_count = stripes
     self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
                                       # storage
     self.attached = True
@@ -554,6 +579,13 @@ class LogicalVolume(BlockDev):
     """Grow the logical volume.
 
     """
+    if self.pe_size is None or self.stripe_count is None:
+      if not self.Attach():
+        _ThrowError("Can't attach to LV during Grow()")
+    full_stripe_size = self.pe_size * self.stripe_count
+    rest = amount % full_stripe_size
+    if rest != 0:
+      amount += full_stripe_size - rest
     # we try multiple algorithms since the 'best' ones might not have
     # space available in the right place, but later ones might (since
     # they have less constraints); also note that only recent LVM
@@ -1609,7 +1641,8 @@ class DRBD8(BaseDRBD):
     if len(self._children) != 2 or None in self._children:
       _ThrowError("drbd%d: cannot grow diskless device", self.minor)
     self._children[0].Grow(amount)
-    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
+    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
+                           "%dm" % (self.size + amount)])
     if result.failed:
       _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
 
diff --git a/lib/bootstrap.py b/lib/bootstrap.py
index a94a9e5ff914def403a3be4ab72d554121b9c030..2b34f4111bc71c3be7aa6847544cb7e258147a80 100644
--- a/lib/bootstrap.py
+++ b/lib/bootstrap.py
@@ -139,6 +139,14 @@ def InitCluster(cluster_name, mac_prefix,
   if config.ConfigWriter.IsCluster():
     raise errors.OpPrereqError("Cluster is already initialised")
 
+  if not enabled_hypervisors:
+    raise errors.OpPrereqError("Enabled hypervisors list must contain at"
+                               " least one member")
+  invalid_hvs = set(enabled_hypervisors) - constants.HYPER_TYPES
+  if invalid_hvs:
+    raise errors.OpPrereqError("Enabled hypervisors contains invalid"
+                               " entries: %s" % invalid_hvs)
+
   hostname = utils.HostInfo()
 
   if hostname.ip.startswith("127."):
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 88d600d076a315e73f16e6089db978b4839bc572..c337cf0a811f674ab982e80088d83f85057729d5 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -1566,6 +1566,13 @@ class LUSetClusterParams(LogicalUnit):
 
     if self.op.enabled_hypervisors is not None:
       self.hv_list = self.op.enabled_hypervisors
+      if not self.hv_list:
+        raise errors.OpPrereqError("Enabled hypervisors list must contain at"
+                                   " least one member")
+      invalid_hvs = set(self.hv_list) - constants.HYPER_TYPES
+      if invalid_hvs:
+        raise errors.OpPrereqError("Enabled hypervisors contains invalid"
+                                   " entries: %s" % invalid_hvs)
     else:
       self.hv_list = cluster.enabled_hypervisors
 
@@ -4736,7 +4743,7 @@ class LUCreateInstance(LogicalUnit):
       disks=[(d["size"], d["mode"]) for d in self.disks],
       bep=self.be_full,
       hvp=self.hv_full,
-      hypervisor=self.op.hypervisor,
+      hypervisor_name=self.op.hypervisor,
     ))
 
     nl = ([self.cfg.GetMasterNode(), self.op.pnode] +
diff --git a/lib/config.py b/lib/config.py
index d7523635177fed59b1c0f7b0d6e455435a75255f..ba4bbdf6137099c77cc8d19ac6506cb231d8b0f7 100644
--- a/lib/config.py
+++ b/lib/config.py
@@ -273,6 +273,20 @@ class ConfigWriter:
     data = self._config_data
     seen_lids = []
     seen_pids = []
+
+    # global cluster checks
+    if not data.cluster.enabled_hypervisors:
+      result.append("enabled hypervisors list doesn't have any entries")
+    invalid_hvs = set(data.cluster.enabled_hypervisors) - constants.HYPER_TYPES
+    if invalid_hvs:
+      result.append("enabled hypervisors contains invalid entries: %s" %
+                    invalid_hvs)
+
+    if data.cluster.master_node not in data.nodes:
+      result.append("cluster has invalid primary node '%s'" %
+                    data.cluster.master_node)
+
+    # per-instance checks
     for instance_name in data.instances:
       instance = data.instances[instance_name]
       if instance.primary_node not in data.nodes:
@@ -1158,32 +1172,6 @@ class ConfigWriter:
       constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
       }
 
-  @locking.ssynchronized(_config_lock)
-  def InitConfig(self, version, cluster_config, master_node_config):
-    """Create the initial cluster configuration.
-
-    It will contain the current node, which will also be the master
-    node, and no instances.
-
-    @type version: int
-    @param version: Configuration version
-    @type cluster_config: objects.Cluster
-    @param cluster_config: Cluster configuration
-    @type master_node_config: objects.Node
-    @param master_node_config: Master node configuration
-
-    """
-    nodes = {
-      master_node_config.name: master_node_config,
-      }
-
-    self._config_data = objects.ConfigData(version=version,
-                                           cluster=cluster_config,
-                                           nodes=nodes,
-                                           instances={},
-                                           serial_no=1)
-    self._WriteConfig()
-
   @locking.ssynchronized(_config_lock, shared=1)
   def GetVGName(self):
     """Return the volume group name.
diff --git a/lib/rapi/baserlib.py b/lib/rapi/baserlib.py
index 0950373fd35f200a99d3e2885f0b997940980e3b..c75dae18232291035c753c818633d2663f6178c4 100644
--- a/lib/rapi/baserlib.py
+++ b/lib/rapi/baserlib.py
@@ -250,6 +250,18 @@ class R_Generic(object):
                                 " '%s' parameter" % (name,))
     return val
 
+  def _checkStringVariable(self, name, default=None):
+    """Return the parsed value of an int argument.
+
+    """
+    val = self.queryargs.get(name, default)
+    if isinstance(val, list):
+      if val:
+        val = val[0]
+      else:
+        val = default
+    return val
+
   def getBodyParameter(self, name, *args):
     """Check and return the value for a given parameter.
 
diff --git a/lib/rapi/connector.py b/lib/rapi/connector.py
index 9c99dcfa6c9ccae0deabd0adbf531e6128f3f5b5..c3c12e6c77450eea2898d34b4c5031fb81001c12 100644
--- a/lib/rapi/connector.py
+++ b/lib/rapi/connector.py
@@ -160,6 +160,8 @@ CONNECTOR.update({
   re.compile(r'^/2/instances/([\w\._-]+)/tags$'): rlib2.R_2_instances_name_tags,
   re.compile(r'^/2/instances/([\w\._-]+)/reboot$'):
       rlib2.R_2_instances_name_reboot,
+  re.compile(r'^/2/instances/([\w\._-]+)/reinstall$'):
+      rlib2.R_2_instances_name_reinstall,
   re.compile(r'^/2/instances/([\w\._-]+)/shutdown$'):
       rlib2.R_2_instances_name_shutdown,
   re.compile(r'^/2/instances/([\w\._-]+)/startup$'):
diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py
index 550692acd51e1be983fe7dd705e127e7de701011..9c8e2a09f582ad9e09d7656871a0b3abb1e5e58c 100644
--- a/lib/rapi/rlib2.py
+++ b/lib/rapi/rlib2.py
@@ -431,6 +431,36 @@ class R_2_instances_name_shutdown(baserlib.R_Generic):
     return baserlib.SubmitJob([op])
 
 
+class R_2_instances_name_reinstall(baserlib.R_Generic):
+  """/2/instances/[instance_name]/reinstall resource.
+
+  Implements an instance reinstall.
+
+  """
+
+  DOC_URI = "/2/instances/[instance_name]/reinstall"
+
+  def POST(self):
+    """Reinstall an instance.
+
+    The URI takes os=name and nostartup=[0|1] optional
+    parameters. By default, the instance will be started
+    automatically.
+
+    """
+    instance_name = self.items[0]
+    ostype = self._checkStringVariable('os')
+    nostartup = self._checkIntVariable('nostartup')
+    ops = [
+      opcodes.OpShutdownInstance(instance_name=instance_name),
+      opcodes.OpReinstallInstance(instance_name=instance_name, os_type=ostype),
+      ]
+    if not nostartup:
+      ops.append(opcodes.OpStartupInstance(instance_name=instance_name,
+                                           force=False))
+    return baserlib.SubmitJob(ops)
+
+
 class _R_Tags(baserlib.R_Generic):
   """ Quasiclass for tagging resources
 
diff --git a/man/gnt-node.sgml b/man/gnt-node.sgml
index 99f80bffd62d957cd01b81439fdfc98fea3d672a..ea02f1944c46e1501f3e690de9508d0da19bf555 100644
--- a/man/gnt-node.sgml
+++ b/man/gnt-node.sgml
@@ -414,13 +414,18 @@
           <varlistentry>
             <term>drained</term>
             <listitem>
-              <simpara>whether the node is drained or not</simpara>
+              <simpara>whether the node is drained or not; the cluster
+              still communicates with drained nodes but excludes them
+              from allocation operations</simpara>
             </listitem>
           </varlistentry>
           <varlistentry>
             <term>offline</term>
             <listitem>
-              <simpara>whether the node is offline or not</simpara>
+              <simpara>whether the node is offline or not; if offline,
+              the cluster does not communicate with offline nodes;
+              useful for nodes that are not reachable in order to
+              avoid delays</simpara>
             </listitem>
           </varlistentry>
           <varlistentry>
diff --git a/scripts/gnt-cluster b/scripts/gnt-cluster
index b41a380c27e21b6937294bc5aedf39dfa89c8968..5e3420126de9543f3dab7f347c0012e030306808 100755
--- a/scripts/gnt-cluster
+++ b/scripts/gnt-cluster
@@ -79,11 +79,6 @@ def InitCluster(opts, args):
     hvparams[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], hvparams[hv])
     utils.ForceDictType(hvparams[hv], constants.HVS_PARAMETER_TYPES)
 
-  for hv in hvlist:
-    if hv not in constants.HYPER_TYPES:
-      ToStderr("invalid hypervisor: %s", hv)
-      return 1
-
   bootstrap.InitCluster(cluster_name=args[0],
                         secondary_ip=opts.secondary_ip,
                         vg_name=vg_name,