Doc fixes for RAPI
[ganeti-local] / lib / bdev.py
index 834d7b3..9d3f08b 100644 (file)
@@ -33,6 +33,38 @@ from ganeti import errors
 from ganeti import constants
 
 
+def _IgnoreError(fn, *args, **kwargs):
+  """Executes the given function, ignoring BlockDeviceErrors.
+
+  This is used in order to simplify the execution of cleanup or
+  rollback functions.
+
+  @rtype: boolean
+  @return: True when fn didn't raise an exception, False otherwise
+
+  """
+  try:
+    fn(*args, **kwargs)
+    return True
+  except errors.BlockDeviceError, err:
+    logging.warning("Caught BlockDeviceError but ignoring: %s" % str(err))
+    return False
+
+
+def _ThrowError(msg, *args):
+  """Log an error to the node daemon and the raise an exception.
+
+  @type msg: string
+  @param msg: the text of the exception
+  @raise errors.BlockDeviceError
+
+  """
+  if args:
+    msg = msg % args
+  logging.error(msg)
+  raise errors.BlockDeviceError(msg)
+
+
 class BlockDev(object):
   """Block device abstract class.
 
@@ -96,7 +128,7 @@ class BlockDev(object):
         this method is idempotent
 
     """
-    return True
+    pass
 
   def Attach(self):
     """Find a device which matches our config and attach to it.
@@ -280,8 +312,7 @@ class LogicalVolume(BlockDev):
     vg_name, lv_name = unique_id
     pvs_info = cls.GetPVInfo(vg_name)
     if not pvs_info:
-      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
-                                    vg_name)
+      _ThrowError("Can't compute PV info for vg %s", vg_name)
     pvs_info.sort()
     pvs_info.reverse()
 
@@ -291,13 +322,13 @@ class LogicalVolume(BlockDev):
     # The size constraint should have been checked from the master before
     # calling the create function.
     if free_size < size:
-      raise errors.BlockDeviceError("Not enough free space: required %s,"
-                                    " available %s" % (size, free_size))
+      _ThrowError("Not enough free space: required %s,"
+                  " available %s", size, free_size)
     result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-n%s" % lv_name,
                            vg_name] + pvlist)
     if result.failed:
-      raise errors.BlockDeviceError("LV create failed (%s): %s" %
-                                    (result.fail_reason, result.output))
+      _ThrowError("LV create failed (%s): %s",
+                  result.fail_reason, result.output)
     return LogicalVolume(unique_id, children)
 
   @staticmethod
@@ -337,14 +368,11 @@ class LogicalVolume(BlockDev):
     """
     if not self.minor and not self.Attach():
       # the LV does not exist
-      return True
+      return
     result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
                            (self._vg_name, self._lv_name)])
     if result.failed:
-      logging.error("Can't lvremove: %s - %s",
-                    result.fail_reason, result.output)
-
-    return not result.failed
+      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
 
   def Rename(self, new_id):
     """Rename this logical volume.
@@ -359,8 +387,7 @@ class LogicalVolume(BlockDev):
                                    (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)
+      _ThrowError("Failed to rename the logical volume: %s", result.output)
     self._lv_name = new_name
     self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
 
@@ -414,9 +441,7 @@ class LogicalVolume(BlockDev):
     """
     result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
     if result.failed:
-      logging.error("Can't activate lv %s: %s", self.dev_path, result.output)
-      return False
-    return self.Attach()
+      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
 
   def Shutdown(self):
     """Shutdown the device.
@@ -425,7 +450,7 @@ class LogicalVolume(BlockDev):
     volumes on shutdown.
 
     """
-    return True
+    pass
 
   def GetSyncStatus(self):
     """Returns the sync status of the device.
@@ -476,25 +501,23 @@ class LogicalVolume(BlockDev):
 
     # remove existing snapshot if found
     snap = LogicalVolume((self._vg_name, snap_name), None)
-    snap.Remove()
+    _IgnoreError(snap.Remove)
 
     pvs_info = self.GetPVInfo(self._vg_name)
     if not pvs_info:
-      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
-                                    self._vg_name)
+      _ThrowError("Can't compute PV info for vg %s", self._vg_name)
     pvs_info.sort()
     pvs_info.reverse()
     free_size, pv_name = pvs_info[0]
     if free_size < size:
-      raise errors.BlockDeviceError("Not enough free space: required %s,"
-                                    " available %s" % (size, free_size))
+      _ThrowError("Not enough free space: required %s,"
+                  " available %s", size, free_size)
 
     result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
                            "-n%s" % snap_name, self.dev_path])
     if result.failed:
-      raise errors.BlockDeviceError("command: %s error: %s - %s" %
-                                    (result.cmd, result.fail_reason,
-                                     result.output))
+      _ThrowError("command: %s error: %s - %s",
+                  result.cmd, result.fail_reason, result.output)
 
     return snap_name
 
@@ -514,9 +537,9 @@ class LogicalVolume(BlockDev):
     result = utils.RunCmd(["lvchange", "--addtag", text,
                            self.dev_path])
     if result.failed:
-      raise errors.BlockDeviceError("Command: %s error: %s - %s" %
-                                    (result.cmd, result.fail_reason,
-                                     result.output))
+      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
+                  result.output)
+
   def Grow(self, amount):
     """Grow the logical volume.
 
@@ -530,8 +553,7 @@ class LogicalVolume(BlockDev):
                              "-L", "+%dm" % amount, self.dev_path])
       if not result.failed:
         return
-    raise errors.BlockDeviceError("Can't grow LV %s: %s" %
-                                  (self.dev_path, result.output))
+    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
 
 
 class DRBD8Status(object):
@@ -541,7 +563,7 @@ class DRBD8Status(object):
 
   """
   UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
-  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+st:([^/]+)/(\S+)"
+  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
                        "\s+ds:([^/]+)/(\S+)\s+.*$")
   SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
                        "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
@@ -617,13 +639,20 @@ class BaseDRBD(BlockDev):
     """Return data from /proc/drbd.
 
     """
-    stat = open(filename, "r")
     try:
-      data = stat.read().splitlines()
-    finally:
-      stat.close()
+      stat = open(filename, "r")
+      try:
+        data = stat.read().splitlines()
+      finally:
+        stat.close()
+    except EnvironmentError, err:
+      if err.errno == errno.ENOENT:
+        _ThrowError("The file %s cannot be opened, check if the module"
+                    " is loaded (%s)", filename, str(err))
+      else:
+        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
     if not data:
-      raise errors.BlockDeviceError("Can't read any data from %s" % filename)
+      _ThrowError("Can't read any data from %s", filename)
     return data
 
   @staticmethod
@@ -736,22 +765,17 @@ class BaseDRBD(BlockDev):
     """
     result = utils.RunCmd(["blockdev", "--getsize", meta_device])
     if result.failed:
-      logging.error("Failed to get device size: %s - %s",
-                    result.fail_reason, result.output)
-      return False
+      _ThrowError("Failed to get device size: %s - %s",
+                  result.fail_reason, result.output)
     try:
       sectors = int(result.stdout)
     except ValueError:
-      logging.error("Invalid output from blockdev: '%s'", result.stdout)
-      return False
+      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
     bytes = sectors * 512
     if bytes < 128 * 1024 * 1024: # less than 128MiB
-      logging.error("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
-      return False
+      _ThrowError("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
     if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
-      logging.error("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
-      return False
-    return True
+      _ThrowError("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
 
   def Rename(self, new_id):
     """Rename a device.
@@ -788,10 +812,9 @@ class DRBD8(BaseDRBD):
     self.major = self._DRBD_MAJOR
     version = self._GetVersion()
     if version['k_major'] != 8 :
-      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
-                                    " requested ganeti usage: kernel is"
-                                    " %s.%s, ganeti wants 8.x" %
-                                    (version['k_major'], version['k_minor']))
+      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
+                  " usage: kernel is %s.%s, ganeti wants 8.x",
+                  version['k_major'], version['k_minor'])
 
     if len(children) not in (0, 2):
       raise ValueError("Invalid configuration data %s" % str(children))
@@ -816,8 +839,7 @@ class DRBD8(BaseDRBD):
     result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
                            "v08", dev_path, "0", "create-md"])
     if result.failed:
-      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
-                                    result.output)
+      _ThrowError("Can't initialize meta device: %s", result.output)
 
   @classmethod
   def _FindUnusedMinor(cls):
@@ -874,15 +896,20 @@ class DRBD8(BaseDRBD):
     # value types
     value = pyp.Word(pyp.alphanums + '_-/.:')
     quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
-    addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() +
-                 number)
+    addr_type = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
+                 pyp.Optional(pyp.Literal("ipv6")).suppress())
+    addr_port = (addr_type + pyp.Word(pyp.nums + '.') +
+                 pyp.Literal(':').suppress() + number)
     # meta device, extended syntax
     meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
                   number + pyp.Word(']').suppress())
+    # device name, extended syntax
+    device_value = pyp.Literal("minor").suppress() + number
 
     # a statement
     stmt = (~rbrace + keyword + ~lbrace +
-            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value) +
+            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value ^
+                         device_value) +
             pyp.Optional(defa) + semi +
             pyp.Optional(pyp.restOfLine).suppress())
 
@@ -929,8 +956,7 @@ class DRBD8(BaseDRBD):
     try:
       results = bnf.parseString(out)
     except pyp.ParseException, err:
-      raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
-                                    str(err))
+      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
 
     # and massage the results into our desired format
     for section in results:
@@ -1013,8 +1039,7 @@ class DRBD8(BaseDRBD):
             backend, meta, "0", "-e", "detach", "--create-device"]
     result = utils.RunCmd(args)
     if result.failed:
-      logging.error("Can't attach local disk: %s", result.output)
-    return not result.failed
+      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
 
   @classmethod
   def _AssembleNet(cls, minor, net_info, protocol,
@@ -1026,7 +1051,8 @@ class DRBD8(BaseDRBD):
     if None in net_info:
       # we don't want network connection and actually want to make
       # sure its shutdown
-      return cls._ShutdownNet(minor)
+      cls._ShutdownNet(minor)
+      return
 
     # Workaround for a race condition. When DRBD is doing its dance to
     # establish a connection with its peer, it also sends the
@@ -1048,9 +1074,8 @@ class DRBD8(BaseDRBD):
       args.extend(["-a", hmac, "-x", secret])
     result = utils.RunCmd(args)
     if result.failed:
-      logging.error("Can't setup network for dbrd device: %s - %s",
-                    result.fail_reason, result.output)
-      return False
+      _ThrowError("drbd%d: can't setup network: %s - %s",
+                  minor, result.fail_reason, result.output)
 
     timeout = time.time() + 10
     ok = False
@@ -1066,32 +1091,29 @@ class DRBD8(BaseDRBD):
       ok = True
       break
     if not ok:
-      logging.error("Timeout while configuring network")
-      return False
-    return True
+      _ThrowError("drbd%d: timeout while configuring network", minor)
 
   def AddChildren(self, devices):
     """Add a disk to the DRBD device.
 
     """
     if self.minor is None:
-      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
+      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
+                  self._aminor)
     if len(devices) != 2:
-      raise errors.BlockDeviceError("Need two devices for AddChildren")
+      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
     info = self._GetDevInfo(self._GetShowData(self.minor))
     if "local_dev" in info:
-      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
+      _ThrowError("drbd%d: already attached to a local disk", self.minor)
     backend, meta = devices
     if backend.dev_path is None or meta.dev_path is None:
-      raise errors.BlockDeviceError("Children not ready during AddChildren")
+      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
     backend.Open()
     meta.Open()
-    if not self._CheckMetaSize(meta.dev_path):
-      raise errors.BlockDeviceError("Invalid meta device size")
+    self._CheckMetaSize(meta.dev_path)
     self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
 
-    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
-      raise errors.BlockDeviceError("Can't attach to local storage")
+    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path)
     self._children = devices
 
   def RemoveChildren(self, devices):
@@ -1099,28 +1121,26 @@ class DRBD8(BaseDRBD):
 
     """
     if self.minor is None:
-      raise errors.BlockDeviceError("Can't attach to drbd8 during"
-                                    " RemoveChildren")
+      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
+                  self._aminor)
     # early return if we don't actually have backing storage
     info = self._GetDevInfo(self._GetShowData(self.minor))
     if "local_dev" not in info:
       return
     if len(self._children) != 2:
-      raise errors.BlockDeviceError("We don't have two children: %s" %
-                                    self._children)
+      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
+                  self._children)
     if self._children.count(None) == 2: # we don't actually have children :)
-      logging.error("Requested detach while detached")
+      logging.warning("drbd%d: requested detach while detached", self.minor)
       return
     if len(devices) != 2:
-      raise errors.BlockDeviceError("We need two children in RemoveChildren")
+      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
     for child, dev in zip(self._children, devices):
       if dev != child.dev_path:
-        raise errors.BlockDeviceError("Mismatch in local storage"
-                                      " (%s != %s) in RemoveChildren" %
-                                      (dev, child.dev_path))
+        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
+                    " RemoveChildren", self.minor, dev, child.dev_path)
 
-    if not self._ShutdownLocal(self.minor):
-      raise errors.BlockDeviceError("Can't detach from local storage")
+    self._ShutdownLocal(self.minor)
     self._children = []
 
   @classmethod
@@ -1164,11 +1184,10 @@ class DRBD8(BaseDRBD):
 
     """
     if self.minor is None:
-      raise errors.BlockDeviceError("GetStats() called while not attached")
+      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
     proc_info = self._MassageProcData(self._GetProcData())
     if self.minor not in proc_info:
-      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
-                                    self.minor)
+      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
     return DRBD8Status(proc_info[self.minor])
 
   def GetSyncStatus(self):
@@ -1191,7 +1210,7 @@ class DRBD8(BaseDRBD):
 
     """
     if self.minor is None and not self.Attach():
-      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
+      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
     stats = self.GetProcStatus()
     ldisk = not stats.is_disk_uptodate
     is_degraded = not stats.is_connected
@@ -1214,9 +1233,8 @@ class DRBD8(BaseDRBD):
       cmd.append("-o")
     result = utils.RunCmd(cmd)
     if result.failed:
-      msg = ("Can't make drbd device primary: %s" % result.output)
-      logging.error(msg)
-      raise errors.BlockDeviceError(msg)
+      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
+                  result.output)
 
   def Close(self):
     """Make the local state secondary.
@@ -1225,14 +1243,11 @@ class DRBD8(BaseDRBD):
 
     """
     if self.minor is None and not self.Attach():
-      logging.info("Instance not attached to a device")
-      raise errors.BlockDeviceError("Can't find device")
+      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
     result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
     if result.failed:
-      msg = ("Can't switch drbd device to"
-             " secondary: %s" % result.output)
-      logging.error(msg)
-      raise errors.BlockDeviceError(msg)
+      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
+                  self.minor, result.output)
 
   def DisconnectNet(self):
     """Removes network configuration.
@@ -1249,13 +1264,13 @@ class DRBD8(BaseDRBD):
 
     """
     if self.minor is None:
-      raise errors.BlockDeviceError("DRBD disk not attached in re-attach net")
+      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
 
     if None in (self._lhost, self._lport, self._rhost, self._rport):
-      raise errors.BlockDeviceError("DRBD disk missing network info in"
-                                    " DisconnectNet()")
+      _ThrowError("drbd%d: DRBD disk missing network info in"
+                  " DisconnectNet()", self.minor)
 
-    ever_disconnected = self._ShutdownNet(self.minor)
+    ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor)
     timeout_limit = time.time() + self._NET_RECONFIG_TIMEOUT
     sleep_time = 0.100 # we start the retry time at 100 miliseconds
     while time.time() < timeout_limit:
@@ -1265,22 +1280,23 @@ class DRBD8(BaseDRBD):
       # retry the disconnect, it seems possible that due to a
       # well-time disconnect on the peer, my disconnect command might
       # be ingored and forgotten
-      ever_disconnected = self._ShutdownNet(self.minor) or ever_disconnected
+      ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor) or \
+                          ever_disconnected
       time.sleep(sleep_time)
       sleep_time = min(2, sleep_time * 1.5)
 
     if not status.is_standalone:
       if ever_disconnected:
-        msg = ("Device did not react to the"
+        msg = ("drbd%d: device did not react to the"
                " 'disconnect' command in a timely manner")
       else:
-        msg = ("Can't shutdown network, even after multiple retries")
-      raise errors.BlockDeviceError(msg)
+        msg = "drbd%d: can't shutdown network, even after multiple retries"
+      _ThrowError(msg, self.minor)
 
     reconfig_time = time.time() - timeout_limit + self._NET_RECONFIG_TIMEOUT
     if reconfig_time > 15: # hardcoded alert limit
-      logging.debug("DRBD8.DisconnectNet: detach took %.3f seconds",
-                    reconfig_time)
+      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
+                   self.minor, reconfig_time)
 
   def AttachNet(self, multimaster):
     """Reconnects the network.
@@ -1294,21 +1310,20 @@ class DRBD8(BaseDRBD):
 
     """
     if self.minor is None:
-      raise errors.BlockDeviceError("DRBD disk not attached in AttachNet")
+      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
 
     if None in (self._lhost, self._lport, self._rhost, self._rport):
-      raise errors.BlockDeviceError("DRBD disk missing network info in"
-                                    " AttachNet()")
+      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
 
     status = self.GetProcStatus()
 
     if not status.is_standalone:
-      raise errors.BlockDeviceError("Device is not standalone in AttachNet")
+      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
 
-    return self._AssembleNet(self.minor,
-                             (self._lhost, self._lport,
-                              self._rhost, self._rport),
-                             "C", dual_pri=multimaster)
+    self._AssembleNet(self.minor,
+                      (self._lhost, self._lport, self._rhost, self._rport),
+                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
+                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
 
   def Attach(self):
     """Check if our minor is configured.
@@ -1339,18 +1354,16 @@ class DRBD8(BaseDRBD):
       - if not, we create it from zero
 
     """
-    result = super(DRBD8, self).Assemble()
-    if not result:
-      return result
+    super(DRBD8, self).Assemble()
 
     self.Attach()
     if self.minor is None:
       # local device completely unconfigured
-      return self._FastAssemble()
+      self._FastAssemble()
     else:
       # we have to recheck the local and network status and try to fix
       # the device
-      return self._SlowAssemble()
+      self._SlowAssemble()
 
   def _SlowAssemble(self):
     """Assembles the DRBD device from a (partially) configured device.
@@ -1360,27 +1373,35 @@ class DRBD8(BaseDRBD):
     the attach if can return success.
 
     """
+    net_data = (self._lhost, self._lport, self._rhost, self._rport)
     for minor in (self._aminor,):
       info = self._GetDevInfo(self._GetShowData(minor))
       match_l = self._MatchesLocal(info)
       match_r = self._MatchesNet(info)
+
       if match_l and match_r:
+        # everything matches
         break
+
       if match_l and not match_r and "local_addr" not in info:
-        res_r = self._AssembleNet(minor,
-                                  (self._lhost, self._lport,
-                                   self._rhost, self._rport),
-                                  constants.DRBD_NET_PROTOCOL,
-                                  hmac=constants.DRBD_HMAC_ALG,
-                                  secret=self._secret
-                                  )
-        if res_r:
-          if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
-            break
-      # the weakest case: we find something that is only net attached
-      # even though we were passed some children at init time
+        # disk matches, but not attached to network, attach and recheck
+        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
+                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
+        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
+          break
+        else:
+          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
+                      " show' disagrees", minor)
+
       if match_r and "local_dev" not in info:
-        break
+        # no local disk, but network attached and it matches
+        self._AssembleLocal(minor, self._children[0].dev_path,
+                            self._children[1].dev_path)
+        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
+          break
+        else:
+          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
+                      " show' disagrees", minor)
 
       # this case must be considered only if we actually have local
       # storage, i.e. not in diskless mode, because all diskless
@@ -1392,27 +1413,30 @@ class DRBD8(BaseDRBD):
         # else, even though its local storage is ours; as we own the
         # drbd space, we try to disconnect from the remote peer and
         # reconnect to our correct one
-        if not self._ShutdownNet(minor):
-          raise errors.BlockDeviceError("Device has correct local storage,"
-                                        " wrong remote peer and is unable to"
-                                        " disconnect in order to attach to"
-                                        " the correct peer")
+        try:
+          self._ShutdownNet(minor)
+        except errors.BlockDeviceError, err:
+          _ThrowError("drbd%d: device has correct local storage, wrong"
+                      " remote peer and is unable to disconnect in order"
+                      " to attach to the correct peer: %s", minor, str(err))
         # note: _AssembleNet also handles the case when we don't want
         # local storage (i.e. one or more of the _[lr](host|port) is
         # None)
-        if (self._AssembleNet(minor, (self._lhost, self._lport,
-                                      self._rhost, self._rport),
-                              constants.DRBD_NET_PROTOCOL,
-                              hmac=constants.DRBD_HMAC_ALG,
-                              secret=self._secret) and
-            self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))):
+        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
+                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
+        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
           break
+        else:
+          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
+                      " show' disagrees", minor)
 
     else:
       minor = None
 
     self._SetFromMinor(minor)
-    return minor is not None
+    if minor is None:
+      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
+                  self._aminor)
 
   def _FastAssemble(self):
     """Assemble the drbd device from zero.
@@ -1420,31 +1444,16 @@ class DRBD8(BaseDRBD):
     This is run when in Assemble we detect our minor is unused.
 
     """
-    # TODO: maybe completely tear-down the minor (drbdsetup ... down)
-    # before attaching our own?
     minor = self._aminor
-    need_localdev_teardown = False
     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:
-        return False
-      need_localdev_teardown = True
+      self._AssembleLocal(minor, self._children[0].dev_path,
+                          self._children[1].dev_path)
     if self._lhost and self._lport and self._rhost and self._rport:
-      result = self._AssembleNet(minor,
-                                 (self._lhost, self._lport,
-                                  self._rhost, self._rport),
-                                 constants.DRBD_NET_PROTOCOL,
-                                 hmac=constants.DRBD_HMAC_ALG,
-                                 secret=self._secret)
-      if not result:
-        if need_localdev_teardown:
-          # we will ignore failures from this
-          logging.error("net setup failed, tearing down local device")
-          self._ShutdownAll(minor)
-        return False
+      self._AssembleNet(minor,
+                        (self._lhost, self._lport, self._rhost, self._rport),
+                        constants.DRBD_NET_PROTOCOL,
+                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
     self._SetFromMinor(minor)
-    return True
 
   @classmethod
   def _ShutdownLocal(cls, minor):
@@ -1456,8 +1465,7 @@ class DRBD8(BaseDRBD):
     """
     result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
     if result.failed:
-      logging.error("Can't detach local device: %s", result.output)
-    return not result.failed
+      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
 
   @classmethod
   def _ShutdownNet(cls, minor):
@@ -1468,8 +1476,7 @@ class DRBD8(BaseDRBD):
     """
     result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
     if result.failed:
-      logging.error("Can't shutdown network: %s", result.output)
-    return not result.failed
+      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
 
   @classmethod
   def _ShutdownAll(cls, minor):
@@ -1480,27 +1487,26 @@ class DRBD8(BaseDRBD):
     """
     result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
     if result.failed:
-      logging.error("Can't shutdown drbd device: %s", result.output)
-    return not result.failed
+      _ThrowError("drbd%d: can't shutdown drbd device: %s",
+                  minor, result.output)
 
   def Shutdown(self):
     """Shutdown the DRBD device.
 
     """
     if self.minor is None and not self.Attach():
-      logging.info("DRBD device not attached to a device during Shutdown")
-      return True
-    if not self._ShutdownAll(self.minor):
-      return False
+      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
+      return
+    minor = self.minor
     self.minor = None
     self.dev_path = None
-    return True
+    self._ShutdownAll(minor)
 
   def Remove(self):
     """Stub remove for DRBD devices.
 
     """
-    return self.Shutdown()
+    self.Shutdown()
 
   @classmethod
   def Create(cls, unique_id, children, size):
@@ -1521,14 +1527,13 @@ class DRBD8(BaseDRBD):
     else:
       in_use = False
     if in_use:
-      raise errors.BlockDeviceError("DRBD minor %d already in use at"
-                                    " Create() time" % aminor)
+      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
     meta = children[1]
     meta.Assemble()
     if not meta.Attach():
-      raise errors.BlockDeviceError("Can't attach to meta device")
-    if not cls._CheckMetaSize(meta.dev_path):
-      raise errors.BlockDeviceError("Invalid meta device size")
+      _ThrowError("drbd%d: can't attach to meta device '%s'",
+                  aminor, meta)
+    cls._CheckMetaSize(meta.dev_path)
     cls._InitMeta(aminor, meta.dev_path)
     return cls(unique_id, children)
 
@@ -1537,15 +1542,13 @@ class DRBD8(BaseDRBD):
 
     """
     if self.minor is None:
-      raise errors.ProgrammerError("drbd8: Grow called while not attached")
+      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
     if len(self._children) != 2 or None in self._children:
-      raise errors.BlockDeviceError("Cannot grow diskless DRBD8 device")
+      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
     self._children[0].Grow(amount)
     result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
     if result.failed:
-      raise errors.BlockDeviceError("resize failed for %s: %s" %
-                                    (self.dev_path, result.output))
-    return
+      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
 
 
 class FileStorage(BlockDev):
@@ -1576,9 +1579,7 @@ class FileStorage(BlockDev):
 
     """
     if not os.path.exists(self.dev_path):
-      raise errors.BlockDeviceError("File device '%s' does not exist." %
-                                    self.dev_path)
-    return True
+      _ThrowError("File device '%s' does not exist" % self.dev_path)
 
   def Shutdown(self):
     """Shutdown the device.
@@ -1587,7 +1588,7 @@ class FileStorage(BlockDev):
     the file on shutdown.
 
     """
-    return True
+    pass
 
   def Open(self, force=False):
     """Make the device ready for I/O.
@@ -1612,14 +1613,11 @@ class FileStorage(BlockDev):
     @return: True if the removal was successful
 
     """
-    if not os.path.exists(self.dev_path):
-      return True
     try:
       os.remove(self.dev_path)
-      return True
     except OSError, err:
-      logging.error("Can't remove file '%s': %s", self.dev_path, err)
-      return False
+      if err.errno != errno.ENOENT:
+        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
 
   def Attach(self):
     """Attach to an existing file.
@@ -1643,17 +1641,17 @@ class FileStorage(BlockDev):
     @return: an instance of FileStorage
 
     """
-    # TODO: decide whether we should check for existing files and
-    # abort or not
     if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
       raise ValueError("Invalid configuration data %s" % str(unique_id))
     dev_path = unique_id[1]
+    if os.path.exists(dev_path):
+      _ThrowError("File already existing: %s", dev_path)
     try:
       f = open(dev_path, 'w')
       f.truncate(size * 1024 * 1024)
       f.close()
     except IOError, err:
-      raise errors.BlockDeviceError("Error in file creation: %" % str(err))
+      _ThrowError("Error in file creation: %", str(err))
 
     return FileStorage(unique_id, children)
 
@@ -1690,10 +1688,7 @@ def Assemble(dev_type, unique_id, children):
   if dev_type not in DEV_MAP:
     raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
   device = DEV_MAP[dev_type](unique_id, children)
-  if not device.Assemble():
-    raise errors.BlockDeviceError("Can't find a valid block device for"
-                                  " %s/%s/%s" %
-                                  (dev_type, unique_id, children))
+  device.Assemble()
   return device