From f3e513add620eabd49f86fa31597c47c2ddb9922 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Mon, 29 Oct 2007 15:44:32 +0000
Subject: [PATCH] Implement block device renaming

This patch add code for renaming a device; more precisely, for changing
the unique_id of the device. This means:
  - logical volumes, rename the volume
  - drbd8, change the remote peer

This is needed for the being able to replace disks for drbd8.

Reviewed-by: imsnah
---
 daemons/ganeti-noded |  8 +++++
 lib/backend.py       | 23 ++++++++++++++
 lib/bdev.py          | 72 ++++++++++++++++++++++++++++++++++++++++++++
 lib/rpc.py           | 13 ++++++++
 4 files changed, 116 insertions(+)

diff --git a/daemons/ganeti-noded b/daemons/ganeti-noded
index 665b84c3c..954010645 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 56dab650d..71d3330cf 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 a01f81305..dfa153ee8 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 4153da8f5..790ad5e40 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.
 
-- 
GitLab