Verify: add instance information to node_info
[ganeti-local] / lib / bdev.py
index c7d2a83..8ba5930 100644 (file)
@@ -25,6 +25,7 @@ import re
 import time
 import errno
 import pyparsing as pyp
+import os
 
 from ganeti import utils
 from ganeti import logger
@@ -53,10 +54,6 @@ class BlockDev(object):
     - md arrays are created or assembled and used
     - drbd devices are attached to a local disk/remote peer and made primary
 
-  The status of the device can be examined by `GetStatus()`, which
-  returns a numerical value, depending on the position in the
-  transition stack of the device.
-
   A block device is identified by three items:
     - the /dev path of the device (dynamic)
     - a unique ID of the device (static)
@@ -82,18 +79,6 @@ class BlockDev(object):
   after assembly we'll have our correct major/minor.
 
   """
-  STATUS_UNKNOWN = 0
-  STATUS_EXISTING = 1
-  STATUS_STANDBY = 2
-  STATUS_ONLINE = 3
-
-  STATUS_MAP = {
-    STATUS_UNKNOWN: "unknown",
-    STATUS_EXISTING: "existing",
-    STATUS_STANDBY: "ready for use",
-    STATUS_ONLINE: "online",
-    }
-
   def __init__(self, unique_id, children):
     self._children = children
     self.dev_path = None
@@ -179,12 +164,6 @@ class BlockDev(object):
     """
     raise NotImplementedError
 
-  def GetStatus(self):
-    """Return the status of the device.
-
-    """
-    raise NotImplementedError
-
   def Open(self, force=False):
     """Make the device ready for use.
 
@@ -439,34 +418,6 @@ class LogicalVolume(BlockDev):
     """
     return True
 
-  def GetStatus(self):
-    """Return the status of the device.
-
-    Logical volumes will can be in all four states, although we don't
-    deactivate (lvchange -an) them when shutdown, so STATUS_EXISTING
-    should not be seen for our devices.
-
-    """
-    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
-    if result.failed:
-      logger.Error("Can't display lv: %s" % result.fail_reason)
-      return self.STATUS_UNKNOWN
-    out = result.stdout.strip()
-    # format: type/permissions/alloc/fixed_minor/state/open
-    if len(out) != 6:
-      return self.STATUS_UNKNOWN
-    #writable = (out[1] == "w")
-    active = (out[4] == "a")
-    online = (out[5] == "o")
-    if online:
-      retval = self.STATUS_ONLINE
-    elif active:
-      retval = self.STATUS_STANDBY
-    else:
-      retval = self.STATUS_EXISTING
-
-    return retval
-
   def GetSyncStatus(self):
     """Returns the sync status of the device.
 
@@ -823,17 +774,6 @@ class MDRaid1(BlockDev):
     for dev in orig_devs:
       self._children.remove(dev)
 
-  def GetStatus(self):
-    """Return the status of the device.
-
-    """
-    self.Attach()
-    if self.minor is None:
-      retval = self.STATUS_UNKNOWN
-    else:
-      retval = self.STATUS_ONLINE
-    return retval
-
   def _SetFromMinor(self, minor):
     """Set our parameters based on the given minor.
 
@@ -1553,34 +1493,6 @@ class DRBDev(BaseDRBD):
     is_degraded = client_state != "Connected"
     return sync_percent, est_time, is_degraded, False
 
-  def GetStatus(self):
-    """Compute the status of the DRBD device
-
-    Note that DRBD devices don't have the STATUS_EXISTING state.
-
-    """
-    if self.minor is None and not self.Attach():
-      return self.STATUS_UNKNOWN
-
-    data = self._GetProcData()
-    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
-                       self.minor)
-    for line in data:
-      mresult = match.match(line)
-      if mresult:
-        break
-    else:
-      logger.Error("Can't find myself!")
-      return self.STATUS_UNKNOWN
-
-    state = mresult.group(2)
-    if state == "Primary":
-      result = self.STATUS_ONLINE
-    else:
-      result = self.STATUS_STANDBY
-
-    return result
-
   @staticmethod
   def _ZeroDevice(device):
     """Zero a device.
@@ -2049,34 +1961,6 @@ class DRBD8(BaseDRBD):
     is_degraded = client_state != "Connected"
     return sync_percent, est_time, is_degraded or ldisk, ldisk
 
-  def GetStatus(self):
-    """Compute the status of the DRBD device
-
-    Note that DRBD devices don't have the STATUS_EXISTING state.
-
-    """
-    if self.minor is None and not self.Attach():
-      return self.STATUS_UNKNOWN
-
-    data = self._GetProcData()
-    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
-                       self.minor)
-    for line in data:
-      mresult = match.match(line)
-      if mresult:
-        break
-    else:
-      logger.Error("Can't find myself!")
-      return self.STATUS_UNKNOWN
-
-    state = mresult.group(2)
-    if state == "Primary":
-      result = self.STATUS_ONLINE
-    else:
-      result = self.STATUS_STANDBY
-
-    return result
-
   def Open(self, force=False):
     """Make the local state primary.
 
@@ -2292,11 +2176,124 @@ class DRBD8(BaseDRBD):
     return cls(unique_id, children)
 
 
+class FileStorage(BlockDev):
+  """File device.
+  
+  This class represents the a file storage backend device.
+
+  The unique_id for the file device is a (file_driver, file_path) tuple.
+  
+  """
+  def __init__(self, unique_id, children):
+    """Initalizes a file device backend.
+
+    """
+    if children:
+      raise errors.BlockDeviceError("Invalid setup for file device")
+    super(FileStorage, self).__init__(unique_id, children)
+    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
+      raise ValueError("Invalid configuration data %s" % str(unique_id))
+    self.driver = unique_id[0]
+    self.dev_path = unique_id[1]
+
+  def Assemble(self):
+    """Assemble the device.
+
+    Checks whether the file device exists, raises BlockDeviceError otherwise.
+
+    """
+    if not os.path.exists(self.dev_path):
+      raise errors.BlockDeviceError("File device '%s' does not exist." %
+                                    self.dev_path)
+    return True
+
+  def Shutdown(self):
+    """Shutdown the device.
+
+    This is a no-op for the file type, as we don't deacivate
+    the file on shutdown.
+
+    """
+    return True
+
+  def Open(self, force=False):
+    """Make the device ready for I/O.
+
+    This is a no-op for the file type.
+
+    """
+    pass
+
+  def Close(self):
+    """Notifies that the device will no longer be used for I/O.
+
+    This is a no-op for the file type.
+
+    """
+    pass
+
+  def Remove(self):
+    """Remove the file backing the block device.
+
+    Returns:
+      boolean indicating wheter removal of file was successful or not.
+
+    """
+    if not os.path.exists(self.dev_path):
+      return True
+    try:
+      os.remove(self.dev_path)
+      return True
+    except OSError, err:
+      logger.Error("Can't remove file '%s': %s"
+                   % (self.dev_path, err))
+      return False
+
+  def Attach(self):
+    """Attach to an existing file.
+
+    Check if this file already exists.
+
+    Returns:
+      boolean indicating if file exists or not.
+
+    """
+    if os.path.exists(self.dev_path):
+      return True
+    return False
+
+  @classmethod
+  def Create(cls, unique_id, children, size):
+    """Create a new file.
+
+    Args:
+      children:
+      size: integer size of file in MiB
+
+    Returns:
+      A ganeti.bdev.FileStorage object.
+
+    """
+    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]
+    try:
+      f = open(dev_path, 'w')
+    except IOError, err:
+      raise BlockDeviceError("Could not create '%'" % err)
+    else:
+      f.truncate(size * 1024 * 1024)
+      f.close()
+
+    return FileStorage(unique_id, children)
+
+
 DEV_MAP = {
   constants.LD_LV: LogicalVolume,
   constants.LD_MD_R1: MDRaid1,
   constants.LD_DRBD7: DRBDev,
   constants.LD_DRBD8: DRBD8,
+  constants.LD_FILE: FileStorage,
   }