diff --git a/daemons/ganeti-noded b/daemons/ganeti-noded
index 665b84c3c3d2892b6b36b212ea682fae38d5ead3..954010645386c84fb0a724e6b29d03e82ddb2620 100755
--- a/daemons/ganeti-noded
+++ b/daemons/ganeti-noded
@@ -112,6 +112,14 @@ class ServerObject(pb.Avatar):
     bdev = objects.Disk.FromDict(bdev_s)
     return backend.RemoveBlockDevice(bdev)
 
+  @staticmethod
+  def perspective_blockdev_rename(params):
+    """Remove a block device.
+
+    """
+    devlist = [(objects.Disk.FromDict(ds), uid) for ds, uid in params]
+    return backend.RenameBlockDevices(devlist)
+
   @staticmethod
   def perspective_blockdev_assemble(params):
     """Assemble a block device.
diff --git a/lib/backend.py b/lib/backend.py
index 56dab650d82fb2fb0d159f4bf331f6be872c39ee..71d3330cfbeb1a3c9ac57280a1fda4f770a5c947 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -1337,6 +1337,29 @@ def RemoveExport(export):
   return True
 
 
+def RenameBlockDevices(devlist):
+  """Rename a list of block devices.
+
+  The devlist argument is a list of tuples (disk, new_logical,
+  new_physical). The return value will be a combined boolean result
+  (True only if all renames succeeded).
+
+  """
+  result = True
+  for disk, unique_id in devlist:
+    dev = _RecursiveFindBD(disk)
+    if dev is None:
+      result = False
+      continue
+    try:
+      dev.Rename(unique_id)
+    except errors.BlockDeviceError, err:
+      logger.Error("Can't rename device '%s' to '%s': %s" %
+                   (dev, unique_id, err))
+      result = False
+  return result
+
+
 class HooksRunner(object):
   """Hook runner.
 
diff --git a/lib/bdev.py b/lib/bdev.py
index a01f813051ecfceb55eab2b547869ecfdc38978d..dfa153ee87b34360d514ab5360c7631d0dc3ca14 100644
--- a/lib/bdev.py
+++ b/lib/bdev.py
@@ -172,6 +172,15 @@ class BlockDev(object):
     raise NotImplementedError
 
 
+  def Rename(self, new_id):
+    """Rename this device.
+
+    This may or may not make sense for a given device type.
+
+    """
+    raise NotImplementedError
+
+
   def GetStatus(self):
     """Return the status of the device.
 
@@ -368,6 +377,21 @@ class LogicalVolume(BlockDev):
 
     return not result.failed
 
+  def Rename(self, new_id):
+    """Rename this logical volume.
+
+    """
+    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
+      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
+    new_vg, new_name = new_id
+    if new_vg != self._vg_name:
+      raise errors.ProgrammerError("Can't move a logical volume across"
+                                   " volume groups (from %s to to %s)" %
+                                   (self._vg_name, new_vg))
+    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
+    if result.failed:
+      raise errors.BlockDeviceError("Failed to rename the logical volume: %s" %
+                                    result.output)
 
   def Attach(self):
     """Attach to an existing LV.
@@ -700,6 +724,13 @@ class MDRaid1(BlockDev):
     #TODO: maybe zero superblock on child devices?
     return self.Shutdown()
 
+  def Rename(self, new_id):
+    """Rename a device.
+
+    This is not supported for md raid1 devices.
+
+    """
+    raise errors.ProgrammerError("Can't rename a md raid1 device")
 
   def AddChildren(self, devices):
     """Add new member(s) to the md raid1.
@@ -1056,6 +1087,14 @@ class BaseDRBD(BlockDev):
       return False
     return True
 
+  def Rename(self, new_id):
+    """Rename a device.
+
+    This is not supported for drbd devices.
+
+    """
+    raise errors.ProgrammerError("Can't rename a drbd device")
+
 
 class DRBDev(BaseDRBD):
   """DRBD block device.
@@ -1562,6 +1601,7 @@ class DRBDev(BaseDRBD):
     """
     return self.Shutdown()
 
+
 class DRBD8(BaseDRBD):
   """DRBD v8.x block device.
 
@@ -2041,6 +2081,17 @@ class DRBD8(BaseDRBD):
     self._SetFromMinor(minor)
     return True
 
+  @classmethod
+  def _ShutdownNet(cls, minor):
+    """Disconnect from the remote peer.
+
+    This fails if we don't have a local device.
+
+    """
+    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
+    logger.Error("Can't shutdown network: %s" % result.output)
+    return not result.failed
+
   @classmethod
   def _ShutdownAll(cls, minor):
     """Deactivate the device.
@@ -2066,6 +2117,27 @@ class DRBD8(BaseDRBD):
     self.dev_path = None
     return True
 
+  def Rename(self, new_uid):
+    """Re-connect this device to another peer.
+
+    """
+    if self.minor is None:
+      raise errors.BlockDeviceError("Device not attached during rename")
+    if self._rhost is not None:
+      # this means we did have a host when we attached, so we are connected
+      if not self._ShutdownNet(self.minor):
+        raise errors.BlockDeviceError("Can't disconnect from remote peer")
+      old_id = self.unique_id
+    else:
+      old_id = None
+    self.unique_id = new_uid
+    if not self._AssembleNet(self.minor, self.unique_id, "C"):
+      logger.Error("Can't attach to new peer!")
+      if self.old_id is not None:
+        self._AssembleNet(self.minor, old_id, "C")
+      self.unique_id = old_id
+      raise errors.BlockDeviceError("Can't attach to new peer")
+
   def Remove(self):
     """Stub remove for DRBD devices.
 
diff --git a/lib/rpc.py b/lib/rpc.py
index 4153da8f58d9628863275c54883a3de594a76309..790ad5e404ccb0c71d544973b364611b86f31043 100644
--- a/lib/rpc.py
+++ b/lib/rpc.py
@@ -514,6 +514,19 @@ def call_blockdev_remove(node, bdev):
   return c.getresult().get(node, False)
 
 
+def call_blockdev_rename(node, devlist):
+  """Request rename of the given block devices.
+
+  This is a single-node call.
+
+  """
+  params = [(d.ToDict(), uid) for d, uid in devlist]
+  c = Client("blockdev_rename", params)
+  c.connect(node)
+  c.run()
+  return c.getresult().get(node, False)
+
+
 def call_blockdev_assemble(node, disk, on_primary):
   """Request assembling of a given block device.