diff --git a/NEWS b/NEWS
index ccc5391d146d6940d6c98b6abf9d73eda5060374..12e26c35cf2c64a4da058cac387d966d4827bcb5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,22 @@
 News
 ====
 
+Version 2.3.0 rc1
+-----------------
+
+*(Released Fri, 19 Nov 2010)*
+
+A number of bugfixes and documentation updates:
+
+- Update ganeti-os-interface documentation
+- Fixed a bug related to duplicate MACs or similar items which should be
+  unique
+- Fix breakage in OS state modify
+- Reinstall instance: disallow offline secondaries (fixes bug related to
+  OS changing but reinstall failing)
+- plus all the other fixes between 2.2.1 and 2.2.2
+
+
 Version 2.3.0 rc0
 -----------------
 
@@ -44,6 +60,20 @@ Version 2.3.0 rc0
 - Infrastructure changes for node group support in future versions
 
 
+Version 2.2.2
+-------------
+
+*(Released Fri, 19 Nov 2010)*
+
+A few small bugs fixed, and some improvements to the build system:
+
+- Fix documentation regarding conversion to drbd
+- Fix validation of parameters in cluster modify (``gnt-cluster modify
+  -B``)
+- Fix error handling in node modify with multiple changes
+- Allow remote imports without checked names
+
+
 Version 2.2.1
 -------------
 
@@ -234,6 +264,21 @@ Version 2.2.0 beta 0
   see the ``ganeti-os-interface(7)`` manpage and look for
   ``EXP_SIZE_FD``
 
+
+Version 2.1.8
+-------------
+
+*(Released Tue, 16 Nov 2010)*
+
+Some more bugfixes. Unless critical bugs occur, this will be the last
+2.1 release:
+
+- Fix case of MAC special-values
+- Fix mac checker regex
+- backend: Fix typo causing β€œout of range” error
+- Add missing --units in gnt-instance list man page
+
+
 Version 2.1.7
 -------------
 
diff --git a/configure.ac b/configure.ac
index afae4b4254239424700b4eda3ba6ede2d0564736..1595d9dfde51f6b244797c33e6d4be0a7c092c4d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
 m4_define([gnt_version_major], [2])
 m4_define([gnt_version_minor], [3])
 m4_define([gnt_version_revision], [0])
-m4_define([gnt_version_suffix], [~rc0])
+m4_define([gnt_version_suffix], [~rc1])
 m4_define([gnt_version_full],
           m4_format([%d.%d.%d%s],
                     gnt_version_major, gnt_version_minor,
diff --git a/doc/admin.rst b/doc/admin.rst
index 9e90df8590902f28d8b1cc101803ed59dbbfcff2..3daf408650221995f4acc9ff0d8e49042d076a7e 100644
--- a/doc/admin.rst
+++ b/doc/admin.rst
@@ -542,7 +542,7 @@ modify`` command::
 
   # later convert it to redundant
   gnt-instance stop INSTANCE
-  gnt-instance modify -t drbd INSTANCE
+  gnt-instance modify -t drbd -n NEW_SECONDARY INSTANCE
   gnt-instance start INSTANCE
 
   # and convert it back
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index babc361a99bc41bec5653fde6d60b55ccb5e99fb..5a0e43685faffadc80a7623f11e39f2a3db424f1 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -593,17 +593,19 @@ def _CheckGlobalHvParams(params):
     raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
 
 
-def _CheckNodeOnline(lu, node):
+def _CheckNodeOnline(lu, node, msg=None):
   """Ensure that a given node is online.
 
   @param lu: the LU on behalf of which we make the check
   @param node: the node to check
+  @param msg: if passed, should be a message to replace the default one
   @raise errors.OpPrereqError: if the node is offline
 
   """
+  if msg is None:
+    msg = "Can't use offline node"
   if lu.cfg.GetNodeInfo(node).offline:
-    raise errors.OpPrereqError("Can't use offline node %s" % node,
-                               errors.ECODE_STATE)
+    raise errors.OpPrereqError("%s: %s" % (msg, node), errors.ECODE_STATE)
 
 
 def _CheckNodeNotDrained(lu, node):
@@ -2575,8 +2577,7 @@ class LUSetClusterParams(LogicalUnit):
             ht.TNone)),
     ("hvparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
                               ht.TNone)),
-    ("beparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
-                              ht.TNone)),
+    ("beparams", None, ht.TOr(ht.TDict, ht.TNone)),
     ("os_hvp", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
                             ht.TNone)),
     ("osparams", None, ht.TOr(ht.TDictOf(ht.TNonEmptyString, ht.TDict),
@@ -2883,14 +2884,14 @@ class LUSetClusterParams(LogicalUnit):
       for key, val in mods:
         if key == constants.DDM_ADD:
           if val in lst:
-            feedback_fn("OS %s already in %s, ignoring", val, desc)
+            feedback_fn("OS %s already in %s, ignoring" % (val, desc))
           else:
             lst.append(val)
         elif key == constants.DDM_REMOVE:
           if val in lst:
             lst.remove(val)
           else:
-            feedback_fn("OS %s not found in %s, ignoring", val, desc)
+            feedback_fn("OS %s not found in %s, ignoring" % (val, desc))
         else:
           raise errors.ProgrammerError("Invalid modification '%s'" % key)
 
@@ -5009,7 +5010,11 @@ class LUReinstallInstance(LogicalUnit):
     instance = self.cfg.GetInstanceInfo(self.op.instance_name)
     assert instance is not None, \
       "Cannot retrieve locked instance %s" % self.op.instance_name
-    _CheckNodeOnline(self, instance.primary_node)
+    _CheckNodeOnline(self, instance.primary_node, "Instance primary node"
+                     " offline, cannot reinstall")
+    for node in instance.secondary_nodes:
+      _CheckNodeOnline(self, node, "Instance secondary node offline,"
+                       " cannot reinstall")
 
     if instance.disk_template == constants.DT_DISKLESS:
       raise errors.OpPrereqError("Instance '%s' has no disks" %
diff --git a/lib/config.py b/lib/config.py
index c5d1a6ab091a5accf3b615e907fefc50f8fcff76..669dadafbe440cc179c8c5e8b6d6dfae0bc02a2b 100644
--- a/lib/config.py
+++ b/lib/config.py
@@ -81,15 +81,15 @@ class TemporaryReservationManager:
     self._ec_reserved = {}
 
   def Reserved(self, resource):
-    for holder_reserved in self._ec_reserved.items():
+    for holder_reserved in self._ec_reserved.values():
       if resource in holder_reserved:
         return True
     return False
 
   def Reserve(self, ec_id, resource):
     if self.Reserved(resource):
-      raise errors.ReservationError("Duplicate reservation for resource: %s." %
-                                    (resource))
+      raise errors.ReservationError("Duplicate reservation for resource '%s'"
+                                    % str(resource))
     if ec_id not in self._ec_reserved:
       self._ec_reserved[ec_id] = set([resource])
     else:
diff --git a/man/gnt-instance.rst b/man/gnt-instance.rst
index bc1281122e744db96642c23ed356a8459806d265..5e8d8fe23a5280e7d522d9c1668ddfd525438e04 100644
--- a/man/gnt-instance.rst
+++ b/man/gnt-instance.rst
@@ -834,7 +834,7 @@ MODIFY
 | [-B *BACKEND\_PARAMETERS*]
 | [--net add*[:options]* \| --net remove \| --net *N:options*]
 | [--disk add:size=*SIZE* \| --disk remove \| --disk *N*:mode=*MODE*]
-| [-t {plain \| drbd}]
+| [-t plain | -t drbd -n *new_secondary*]
 | [--os-name=*OS* [--force-variant]]
 | [--submit]
 | {*instance*}
@@ -849,9 +849,10 @@ name=value[,...]. For details which options can be specified, see
 the **add** command.
 
 The ``-t`` option will change the disk template of the instance.
-Currently only conversions between the plain and drbd disk
-templates are supported, and the instance must be stopped before
-attempting the conversion.
+Currently only conversions between the plain and drbd disk templates
+are supported, and the instance must be stopped before attempting the
+conversion. When changing from the plain to the drbd disk template, a
+new secondary node must be specified via the ``-n`` option.
 
 The ``--disk add:size=``*SIZE* option adds a disk to the instance. The
 ``--disk remove`` option will remove the last disk of the
diff --git a/qa/ganeti-qa.py b/qa/ganeti-qa.py
index aace2348022105cbd2c3d9257424a7a988cb2510..af8ddb59698e20dca806a04b4bd2ef941185b92b 100755
--- a/qa/ganeti-qa.py
+++ b/qa/ganeti-qa.py
@@ -128,6 +128,10 @@ def RunClusterTests():
   if qa_config.TestEnabled('cluster-reserved-lvs'):
     RunTest(qa_cluster.TestClusterReservedLvs)
 
+  if qa_config.TestEnabled("cluster-modify"):
+    RunTest(qa_cluster.TestClusterModifyBe)
+    # TODO: add more cluster modify tests
+
   if qa_config.TestEnabled('cluster-rename'):
     RunTest(qa_cluster.TestClusterRename)
 
diff --git a/qa/qa-sample.json b/qa/qa-sample.json
index 3b1be0df60ecb7e6258f4ffb8fb15077f7508134..57fd3539f1655461535aeeb32ae4788a61a90c34 100644
--- a/qa/qa-sample.json
+++ b/qa/qa-sample.json
@@ -51,6 +51,7 @@
     "cluster-destroy": true,
     "cluster-rename": true,
     "cluster-reserved-lvs": true,
+    "cluster-modify": true,
 
     "node-info": true,
     "node-volumes": true,
diff --git a/qa/qa_cluster.py b/qa/qa_cluster.py
index bab5bc6280676dae241645e87bcdf7c9a9a01306..f615a736bb35a7305bc349663dbf3408dca7fd27 100644
--- a/qa/qa_cluster.py
+++ b/qa/qa_cluster.py
@@ -171,6 +171,34 @@ def TestClusterReservedLvs():
                          utils.ShellQuoteArgs(cmd)).wait(), rcode)
 
 
+def TestClusterModifyBe():
+  """gnt-cluster modify -B"""
+  master = qa_config.GetMasterNode()
+
+  for rcode, cmd in [
+    # mem
+    (0, ["gnt-cluster", "modify", "-B", "memory=256"]),
+    (0, ["sh", "-c", "gnt-cluster info|grep '^ *memory: 256$'"]),
+    (1, ["gnt-cluster", "modify", "-B", "memory=a"]),
+    (0, ["gnt-cluster", "modify", "-B", "memory=128"]),
+    (0, ["sh", "-c", "gnt-cluster info|grep '^ *memory: 128$'"]),
+    # vcpus
+    (0, ["gnt-cluster", "modify", "-B", "vcpus=4"]),
+    (0, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 4$'"]),
+    (1, ["gnt-cluster", "modify", "-B", "vcpus=a"]),
+    (0, ["gnt-cluster", "modify", "-B", "vcpus=1"]),
+    (0, ["sh", "-c", "gnt-cluster info|grep '^ *vcpus: 1$'"]),
+    # auto_balance
+    (0, ["gnt-cluster", "modify", "-B", "auto_balance=False"]),
+    (0, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: False$'"]),
+    (1, ["gnt-cluster", "modify", "-B", "auto_balance=1"]),
+    (0, ["gnt-cluster", "modify", "-B", "auto_balance=True"]),
+    (0, ["sh", "-c", "gnt-cluster info|grep '^ *auto_balance: True$'"]),
+    ]:
+    AssertEqual(StartSSH(master['primary'],
+                         utils.ShellQuoteArgs(cmd)).wait(), rcode)
+
+
 def TestClusterInfo():
   """gnt-cluster info"""
   master = qa_config.GetMasterNode()
diff --git a/qa/qa_os.py b/qa/qa_os.py
index 143fedc53df3eb575659e9804daeabcd13767202..e6e259aadb8dc3c7408aa7d86d1cede5f29d6fe0 100644
--- a/qa/qa_os.py
+++ b/qa/qa_os.py
@@ -86,6 +86,9 @@ def _TestOsStates():
       new_cmd = cmd + ["--%s" % param, val, _TEMP_OS_NAME]
       AssertEqual(StartSSH(master["primary"],
                            utils.ShellQuoteArgs(new_cmd)).wait(), 0)
+      # check that double-running the command is OK
+      AssertEqual(StartSSH(master["primary"],
+                           utils.ShellQuoteArgs(new_cmd)).wait(), 0)
 
 
 def _SetupTempOs(node, dir, valid):
diff --git a/test/ganeti.config_unittest.py b/test/ganeti.config_unittest.py
index 984bfff629eb24530f91d7a4c3b359c348be1b48..51284a7827412628e15b7d1cc69b80425a850e28 100755
--- a/test/ganeti.config_unittest.py
+++ b/test/ganeti.config_unittest.py
@@ -37,6 +37,8 @@ from ganeti import objects
 from ganeti import utils
 from ganeti import netutils
 
+from ganeti.config import TemporaryReservationManager
+
 import testutils
 import mocks
 
@@ -186,5 +188,23 @@ class TestConfigRunner(unittest.TestCase):
                       CheckSyntax, {mode: m_bridged, link: ''})
 
 
+class TestTRM(unittest.TestCase):
+  EC_ID = 1
+
+  def testEmpty(self):
+    t = TemporaryReservationManager()
+    t.Reserve(self.EC_ID, "a")
+    self.assertFalse(t.Reserved(self.EC_ID))
+    self.assertTrue(t.Reserved("a"))
+    self.assertEqual(len(t.GetReserved()), 1)
+
+  def testDuplicate(self):
+    t = TemporaryReservationManager()
+    t.Reserve(self.EC_ID, "a")
+    self.assertRaises(errors.ReservationError, t.Reserve, 2, "a")
+    t.DropECReservations(self.EC_ID)
+    self.assertFalse(t.Reserved("a"))
+
+
 if __name__ == '__main__':
   testutils.GanetiTestProgram()