commit 153d9724f6045a7f08d1e64352ff68993408fa01
Author: Iustin Pop <iustin@google.com>
Date:   Thu Oct 25 11:22:39 2007 +0000

    Modify two mirror-device related rpc calls
    
    The two calls mirror_addchild and mirror_removechild take only one child
    for addition/removal. While this is enough for our md usage, for local
    disk replacement in drbd8, we need to be able to specify both the data
    and metadata device. This patch modifies these two rpc calls (and their
    backend implementation and their usage in cmdlib) to take a list of
    children to add/remove.
    
    Reviewed-by: imsnah

diff --git a/daemons/ganeti-noded b/daemons/ganeti-noded
index 12f6b7a..665b84c 100755
--- a/daemons/ganeti-noded
+++ b/daemons/ganeti-noded
@@ -135,7 +135,7 @@ class ServerObject(pb.Avatar):
     return backend.ShutdownBlockDevice(bdev)
 
   @staticmethod
-  def perspective_blockdev_addchild(params):
+  def perspective_blockdev_addchildren(params):
     """Add a child to a mirror device.
 
     Note: this is only valid for mirror devices. It's the caller's duty
@@ -144,13 +144,13 @@ class ServerObject(pb.Avatar):
     """
     bdev_s, ndev_s = params
     bdev = objects.Disk.FromDict(bdev_s)
-    ndev = objects.Disk.FromDict(ndev_s)
-    if bdev is None or ndev is None:
+    ndevs = [objects.Disk.FromDict(disk_s) for disk_s in ndev_s]
+    if bdev is None or ndevs.count(None) > 0:
       raise ValueError("can't unserialize data!")
-    return backend.MirrorAddChild(bdev, ndev)
+    return backend.MirrorAddChildren(bdev, ndevs)
 
   @staticmethod
-  def perspective_blockdev_removechild(params):
+  def perspective_blockdev_removechildren(params):
     """Remove a child from a mirror device.
 
     This is only valid for mirror devices, of course. It's the callers
@@ -159,10 +159,10 @@ class ServerObject(pb.Avatar):
     """
     bdev_s, ndev_s = params
     bdev = objects.Disk.FromDict(bdev_s)
-    ndev = objects.Disk.FromDict(ndev_s)
-    if bdev is None or ndev is None:
+    ndevs = [objects.Disk.FromDict(disk_s) for disk_s in ndev_s]
+    if bdev is None or ndevs.count(None) > 0:
       raise ValueError("can't unserialize data!")
-    return backend.MirrorRemoveChild(bdev, ndev)
+    return backend.MirrorRemoveChildren(bdev, ndevs)
 
   @staticmethod
   def perspective_blockdev_getmirrorstatus(params):
diff --git a/lib/backend.py b/lib/backend.py
index 78ce381..56dab65 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -768,35 +768,33 @@ def ShutdownBlockDevice(disk):
   return result
 
 
-def MirrorAddChild(md_cdev, new_cdev):
-  """Extend an MD raid1 array.
+def MirrorAddChildren(parent_cdev, new_cdevs):
+  """Extend a mirrored block device.
 
   """
-  md_bdev = _RecursiveFindBD(md_cdev, allow_partial=True)
-  if md_bdev is None:
-    logger.Error("Can't find md device")
+  parent_bdev = _RecursiveFindBD(parent_cdev, allow_partial=True)
+  if parent_bdev is None:
+    logger.Error("Can't find parent device")
     return False
-  new_bdev = _RecursiveFindBD(new_cdev)
-  if new_bdev is None:
-    logger.Error("Can't find new device to add")
+  new_bdevs = [_RecursiveFindBD(disk) for disk in new_cdevs]
+  if new_bdevs.count(None) > 0:
+    logger.Error("Can't find new device(s) to add")
     return False
-  new_bdev.Open()
-  md_bdev.AddChild(new_bdev)
+  parent_bdev.AddChildren(new_bdevs)
   return True
 
 
-def MirrorRemoveChild(md_cdev, new_cdev):
-  """Reduce an MD raid1 array.
+def MirrorRemoveChildren(parent_cdev, new_cdevs):
+  """Shrink a mirrored block device.
 
   """
-  md_bdev = _RecursiveFindBD(md_cdev)
-  if md_bdev is None:
+  parent_bdev = _RecursiveFindBD(parent_cdev)
+  if parent_bdev is None:
     return False
-  new_bdev = _RecursiveFindBD(new_cdev)
-  if new_bdev is None:
+  new_bdevs = [_RecursiveFindBD(disk) for disk in new_cdevs]
+  if new_bdevs.count(None) > 0:
     return False
-  new_bdev.Open()
-  md_bdev.RemoveChild(new_bdev.dev_path)
+  parent_bdev.RemoveChildren(new_bdevs)
   return True
 
 
diff --git a/lib/bdev.py b/lib/bdev.py
index 0e8ddf4..a01f813 100644
--- a/lib/bdev.py
+++ b/lib/bdev.py
@@ -701,59 +701,71 @@ class MDRaid1(BlockDev):
     return self.Shutdown()
 
 
-  def AddChild(self, device):
-    """Add a new member to the md raid1.
+  def AddChildren(self, devices):
+    """Add new member(s) to the md raid1.
 
     """
     if self.minor is None and not self.Attach():
       raise errors.BlockDeviceError("Can't attach to device")
-    if device.dev_path is None:
-      raise errors.BlockDeviceError("New child is not initialised")
-    result = utils.RunCmd(["mdadm", "-a", self.dev_path, device.dev_path])
+
+    args = ["mdadm", "-a", self.dev_path]
+    for dev in devices:
+      if dev.dev_path is None:
+        raise errors.BlockDeviceError("Child '%s' is not initialised" % dev)
+      dev.Open()
+      args.append(dev.dev_path)
+    result = utils.RunCmd(args)
     if result.failed:
       raise errors.BlockDeviceError("Failed to add new device to array: %s" %
                                     result.output)
-    new_len = len(self._children) + 1
+    new_len = len(self._children) + len(devices)
     result = utils.RunCmd(["mdadm", "--grow", self.dev_path, "-n", new_len])
     if result.failed:
       raise errors.BlockDeviceError("Can't grow md array: %s" %
                                     result.output)
-    self._children.append(device)
+    self._children.extend(devices)
 
 
-  def RemoveChild(self, dev_path):
-    """Remove member from the md raid1.
+  def RemoveChildren(self, devices):
+    """Remove member(s) from the md raid1.
 
     """
     if self.minor is None and not self.Attach():
       raise errors.BlockDeviceError("Can't attach to device")
-    if len(self._children) == 1:
-      raise errors.BlockDeviceError("Can't reduce member when only one"
-                                    " child left")
-    for device in self._children:
-      if device.dev_path == dev_path:
-        break
-    else:
-      raise errors.BlockDeviceError("Can't find child with this path")
-    new_len = len(self._children) - 1
-    result = utils.RunCmd(["mdadm", "-f", self.dev_path, dev_path])
+    new_len = len(self._children) - len(devices)
+    if new_len < 1:
+      raise errors.BlockDeviceError("Can't reduce to less than one child")
+    args = ["mdadm", "-f", self.dev_path]
+    orig_devs = []
+    for dev in devices:
+      args.append(dev.dev_path)
+      for c in self._children:
+        if c.dev_path == dev.dev_path:
+          orig_devs.append(c)
+          break
+      else:
+        raise errors.BlockDeviceError("Can't find device '%s' for removal" %
+                                      dev)
+    result = utils.RunCmd(args)
     if result.failed:
-      raise errors.BlockDeviceError("Failed to mark device as failed: %s" %
+      raise errors.BlockDeviceError("Failed to mark device(s) as failed: %s" %
                                     result.output)
 
     # it seems here we need a short delay for MD to update its
     # superblocks
     time.sleep(0.5)
-    result = utils.RunCmd(["mdadm", "-r", self.dev_path, dev_path])
+    args[1] = "-r"
+    result = utils.RunCmd(args)
     if result.failed:
-      raise errors.BlockDeviceError("Failed to remove device from array:"
-                                        " %s" % result.output)
+      raise errors.BlockDeviceError("Failed to remove device(s) from array:"
+                                    " %s" % result.output)
     result = utils.RunCmd(["mdadm", "--grow", "--force", self.dev_path,
                            "-n", new_len])
     if result.failed:
       raise errors.BlockDeviceError("Can't shrink md array: %s" %
                                     result.output)
-    self._children.remove(device)
+    for dev in orig_devs:
+      self._children.remove(dev)
 
 
   def GetStatus(self):
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index ab433ed..04612d7 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -3223,8 +3223,8 @@ class LUAddMDDRBDComponent(LogicalUnit):
     # the device exists now
     # call the primary node to add the mirror to md
     logger.Info("adding new mirror component to md")
-    if not rpc.call_blockdev_addchild(instance.primary_node,
-                                           disk, new_drbd):
+    if not rpc.call_blockdev_addchildren(instance.primary_node,
+                                         disk, [new_drbd]):
       logger.Error("Can't add mirror compoment to md!")
       self.cfg.SetDiskID(new_drbd, remote_node)
       if not rpc.call_blockdev_remove(remote_node, new_drbd):
@@ -3316,8 +3316,8 @@ class LURemoveMDDRBDComponent(LogicalUnit):
     child = self.child
     logger.Info("remove mirror component")
     self.cfg.SetDiskID(disk, instance.primary_node)
-    if not rpc.call_blockdev_removechild(instance.primary_node,
-                                              disk, child):
+    if not rpc.call_blockdev_removechildren(instance.primary_node,
+                                            disk, [child]):
       raise errors.OpExecError("Can't remove child from mirror.")
 
     for node in child.logical_id[:2]:
@@ -3427,8 +3427,8 @@ class LUReplaceDisks(LogicalUnit):
       # the device exists now
       # call the primary node to add the mirror to md
       logger.Info("adding new mirror component to md")
-      if not rpc.call_blockdev_addchild(instance.primary_node, dev,
-                                        new_drbd):
+      if not rpc.call_blockdev_addchildren(instance.primary_node, dev,
+                                           [new_drbd]):
         logger.Error("Can't add mirror compoment to md!")
         cfg.SetDiskID(new_drbd, remote_node)
         if not rpc.call_blockdev_remove(remote_node, new_drbd):
@@ -3462,8 +3462,8 @@ class LUReplaceDisks(LogicalUnit):
       dev, child, new_drbd = iv_names[name]
       logger.Info("remove mirror %s component" % name)
       cfg.SetDiskID(dev, instance.primary_node)
-      if not rpc.call_blockdev_removechild(instance.primary_node,
-                                                dev, child):
+      if not rpc.call_blockdev_removechildren(instance.primary_node,
+                                              dev, [child]):
         logger.Error("Can't remove child from mirror, aborting"
                      " *this device cleanup*.\nYou need to cleanup manually!!")
         continue
diff --git a/lib/rpc.py b/lib/rpc.py
index f545e79..4153da8 100644
--- a/lib/rpc.py
+++ b/lib/rpc.py
@@ -539,27 +539,27 @@ def call_blockdev_shutdown(node, disk):
   return c.getresult().get(node, False)
 
 
-def call_blockdev_addchild(node, bdev, ndev):
-  """Request adding a new child to a (mirroring) device.
+def call_blockdev_addchildren(node, bdev, ndevs):
+  """Request adding a list of children to a (mirroring) device.
 
   This is a single-node call.
 
   """
-  params = [bdev.ToDict(), ndev.ToDict()]
-  c = Client("blockdev_addchild", params)
+  params = [bdev.ToDict(), [disk.ToDict() for disk in ndevs]]
+  c = Client("blockdev_addchildren", params)
   c.connect(node)
   c.run()
   return c.getresult().get(node, False)
 
 
-def call_blockdev_removechild(node, bdev, ndev):
-  """Request removing a new child from a (mirroring) device.
+def call_blockdev_removechildren(node, bdev, ndevs):
+  """Request removing a list of children from a (mirroring) device.
 
   This is a single-node call.
 
   """
-  params = [bdev.ToDict(), ndev.ToDict()]
-  c = Client("blockdev_removechild", params)
+  params = [bdev.ToDict(), [disk.ToDict() for disk in ndevs]]
+  c = Client("blockdev_removechildren", params)
   c.connect(node)
   c.run()
   return c.getresult().get(node, False)
