From fc1dc9d77ea15e8dc07199d9f361ce13f62810d7 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Tue, 6 Nov 2007 17:00:38 +0000
Subject: [PATCH] Allow DRBD8 operation without backing storage

This patch adds the following functionality:
  - DRBD8 devices can assemble without local storage (done by allowing
    None in the list of children, and making DRBD8 to ignore all
    children if any is None)
  - DRBD8 devices can attach (i.e. identify a device) which is not
    connected to backing storage but to the correct network ports; this
    is a rare case in normal operation (it's what would happen if one
    manually detaches the local disk, and the backing LV still exists)

Reviewed-by: imsnah
---
 lib/backend.py | 14 +++++++++++++-
 lib/bdev.py    |  8 +++++++-
 lib/objects.py | 16 ++++++++++++++++
 3 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/lib/backend.py b/lib/backend.py
index 479cea256..f85ecba95 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -722,8 +722,20 @@ def _RecursiveAssembleBD(disk, owner, as_primary):
   """
   children = []
   if disk.children:
+    mcn = disk.ChildrenNeeded()
+    if mcn == -1:
+      mcn = 0 # max number of Nones allowed
+    else:
+      mcn = len(disk.children) - mcn # max number of Nones
     for chld_disk in disk.children:
-      children.append(_RecursiveAssembleBD(chld_disk, owner, as_primary))
+      try:
+        cdev = _RecursiveAssembleBD(chld_disk, owner, as_primary)
+      except errors.BlockDeviceError, err:
+        if children.count(None) > mcn:
+          raise
+        cdev = None
+        logger.Debug("Error in child activation: %s" % str(err))
+      children.append(cdev)
 
   if as_primary or disk.AssembleOnSecondary():
     r_dev = bdev.AttachOrAssemble(disk.dev_type, disk.physical_id, children)
diff --git a/lib/bdev.py b/lib/bdev.py
index 8a80a4c1c..abedc90c2 100644
--- a/lib/bdev.py
+++ b/lib/bdev.py
@@ -1566,6 +1566,8 @@ class DRBD8(BaseDRBD):
   _PARSE_SHOW = None
 
   def __init__(self, unique_id, children):
+    if children and children.count(None) > 0:
+      children = []
     super(DRBD8, self).__init__(unique_id, children)
     self.major = self._DRBD_MAJOR
     [kmaj, kmin, kfix, api, proto] = self._GetVersion()
@@ -2030,6 +2032,10 @@ class DRBD8(BaseDRBD):
                                   "C")
         if res_r and self._MatchesNet(self._GetDevInfo(minor)):
           break
+      # the weakest case: we find something that is only net attached
+      # even though we were passed some children at init time
+      if match_r and "local_dev" not in info:
+        break
     else:
       minor = None
 
@@ -2066,7 +2072,7 @@ class DRBD8(BaseDRBD):
 
     minor = self._FindUnusedMinor()
     need_localdev_teardown = False
-    if self._children[0]:
+    if self._children and self._children[0] and self._children[1]:
       result = self._AssembleLocal(minor, self._children[0].dev_path,
                                    self._children[1].dev_path)
       if not result:
diff --git a/lib/objects.py b/lib/objects.py
index 9d199c577..507e43d55 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -338,6 +338,22 @@ class Disk(ConfigObject):
       return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
     return None
 
+  def ChildrenNeeded(self):
+    """Compute the needed number of children for activation.
+
+    This method will return either -1 (all children) or a positive
+    number denoting the minimum number of children needed for
+    activation (only mirrored devices will usually return >=0).
+
+    Currently, only DRBD8 supports diskless activation (therefore we
+    return 0), for all other we keep the previous semantics and return
+    -1.
+
+    """
+    if self.dev_type == constants.LD_DRBD8:
+      return 0
+    return -1
+
   def GetNodes(self, node):
     """This function returns the nodes this device lives on.
 
-- 
GitLab