Revision 391d0261

b/lib/bdev.py
24 24
import re
25 25
import time
26 26
import errno
27
import stat
27 28
import pyparsing as pyp
28 29
import os
29 30
import logging
......
2069 2070
    return FileStorage(unique_id, children, size)
2070 2071

  
2071 2072

  
2073
class PersistentBlockDevice(BlockDev):
2074
  """A block device with persistent node
2075

  
2076
  May be either directly attached, or exposed through DM (e.g. dm-multipath).
2077
  udev helpers are probably required to give persistent, human-friendly
2078
  names.
2079

  
2080
  For the time being, pathnames are required to lie under /dev.
2081

  
2082
  """
2083
  def __init__(self, unique_id, children, size):
2084
    """Attaches to a static block device.
2085

  
2086
    The unique_id is a path under /dev.
2087

  
2088
    """
2089
    super(PersistentBlockDevice, self).__init__(unique_id, children, size)
2090
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2091
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2092
    self.dev_path = unique_id[1]
2093
    if not os.path.realpath(self.dev_path).startswith('/dev/'):
2094
      raise ValueError("Full path '%s' lies outside /dev" %
2095
                              os.path.realpath(self.dev_path))
2096
    # TODO: this is just a safety guard checking that we only deal with devices
2097
    # we know how to handle. In the future this will be integrated with
2098
    # external storage backends and possible values will probably be collected
2099
    # from the cluster configuration.
2100
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
2101
      raise ValueError("Got persistent block device of invalid type: %s" %
2102
                       unique_id[0])
2103

  
2104
    self.major = self.minor = None
2105
    self.Attach()
2106

  
2107
  @classmethod
2108
  def Create(cls, unique_id, children, size):
2109
    """Create a new device
2110

  
2111
    This is a noop, we only return a PersistentBlockDevice instance
2112

  
2113
    """
2114
    return PersistentBlockDevice(unique_id, children, 0)
2115

  
2116
  def Remove(self):
2117
    """Remove a device
2118

  
2119
    This is a noop
2120

  
2121
    """
2122
    pass
2123

  
2124
  def Rename(self, new_id):
2125
    """Rename this device.
2126

  
2127
    """
2128
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2129

  
2130
  def Attach(self):
2131
    """Attach to an existing block device.
2132

  
2133

  
2134
    """
2135
    self.attached = False
2136
    try:
2137
      st = os.stat(self.dev_path)
2138
    except OSError, err:
2139
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2140
      return False
2141

  
2142
    if not stat.S_ISBLK(st.st_mode):
2143
      logging.error("%s is not a block device", self.dev_path)
2144
      return False
2145

  
2146
    self.major = os.major(st.st_rdev)
2147
    self.minor = os.minor(st.st_rdev)
2148
    self.attached = True
2149

  
2150
    return True
2151

  
2152
  def Assemble(self):
2153
    """Assemble the device.
2154

  
2155
    """
2156
    pass
2157

  
2158
  def Shutdown(self):
2159
    """Shutdown the device.
2160

  
2161
    """
2162
    pass
2163

  
2164
  def Open(self, force=False):
2165
    """Make the device ready for I/O.
2166

  
2167
    """
2168
    pass
2169

  
2170
  def Close(self):
2171
    """Notifies that the device will no longer be used for I/O.
2172

  
2173
    """
2174
    pass
2175

  
2176
  def Grow(self, amount):
2177
    """Grow the logical volume.
2178

  
2179
    """
2180
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2181

  
2182

  
2072 2183
DEV_MAP = {
2073 2184
  constants.LD_LV: LogicalVolume,
2074 2185
  constants.LD_DRBD8: DRBD8,
2186
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2075 2187
  }
2076 2188

  
2077 2189
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
b/lib/cmdlib.py
6621 6621
                                                         disk_index)),
6622 6622
                              mode=disk["mode"])
6623 6623
      disks.append(disk_dev)
6624
  elif template_name == constants.DT_BLOCK:
6625
    if len(secondary_nodes) != 0:
6626
      raise errors.ProgrammerError("Wrong template configuration")
6627

  
6628
    for idx, disk in enumerate(disk_info):
6629
      disk_index = idx + base_index
6630
      disk_dev = objects.Disk(dev_type=constants.LD_BLOCKDEV, size=disk["size"],
6631
                              logical_id=(constants.BLOCKDEV_DRIVER_MANUAL,
6632
                                          disk["adopt"]),
6633
                              iv_name="disk/%d" % disk_index,
6634
                              mode=disk["mode"])
6635
      disks.append(disk_dev)
6636

  
6624 6637
  else:
6625 6638
    raise errors.ProgrammerError("Invalid disk template '%s'" % template_name)
6626 6639
  return disks
......
6847 6860
    constants.DT_DRBD8: sum(d["size"] + 128 for d in disks),
6848 6861
    constants.DT_FILE: None,
6849 6862
    constants.DT_SHARED_FILE: 0,
6863
    constants.DT_BLOCK: 0,
6850 6864
  }
6851 6865

  
6852 6866
  if disk_template not in req_size_dict:
......
6982 6996
      if self.op.mode == constants.INSTANCE_IMPORT:
6983 6997
        raise errors.OpPrereqError("Disk adoption not allowed for"
6984 6998
                                   " instance import", errors.ECODE_INVAL)
6999
    else:
7000
      if self.op.disk_template in constants.DTS_MUST_ADOPT:
7001
        raise errors.OpPrereqError("Disk template %s requires disk adoption,"
7002
                                   " but no 'adopt' parameter given" %
7003
                                   self.op.disk_template,
7004
                                   errors.ECODE_INVAL)
6985 7005

  
6986 7006
    self.adopt_disks = has_adopt
6987 7007

  
......
7584 7604
      req_sizes = _ComputeDiskSizePerVG(self.op.disk_template, self.disks)
7585 7605
      _CheckNodesFreeDiskPerVG(self, nodenames, req_sizes)
7586 7606

  
7587
    else: # instead, we must check the adoption data
7607
    elif self.op.disk_template == constants.DT_PLAIN: # Check the adoption data
7588 7608
      all_lvs = set([i["vg"] + "/" + i["adopt"] for i in self.disks])
7589 7609
      if len(all_lvs) != len(self.disks):
7590 7610
        raise errors.OpPrereqError("Duplicate volume names given for adoption",
......
7620 7640
      for dsk in self.disks:
7621 7641
        dsk["size"] = int(float(node_lvs[dsk["vg"] + "/" + dsk["adopt"]][0]))
7622 7642

  
7643
    elif self.op.disk_template == constants.DT_BLOCK:
7644
      # Normalize and de-duplicate device paths
7645
      all_disks = set([os.path.abspath(i["adopt"]) for i in self.disks])
7646
      if len(all_disks) != len(self.disks):
7647
        raise errors.OpPrereqError("Duplicate disk names given for adoption",
7648
                                   errors.ECODE_INVAL)
7649
      baddisks = [d for d in all_disks
7650
                  if not d.startswith(constants.ADOPTABLE_BLOCKDEV_ROOT)]
7651
      if baddisks:
7652
        raise errors.OpPrereqError("Device node(s) %s lie outside %s and"
7653
                                   " cannot be adopted" %
7654
                                   (", ".join(baddisks),
7655
                                    constants.ADOPTABLE_BLOCKDEV_ROOT),
7656
                                   errors.ECODE_INVAL)
7657

  
7658
      node_disks = self.rpc.call_bdev_sizes([pnode.name],
7659
                                            list(all_disks))[pnode.name]
7660
      node_disks.Raise("Cannot get block device information from node %s" %
7661
                       pnode.name)
7662
      node_disks = node_disks.payload
7663
      delta = all_disks.difference(node_disks.keys())
7664
      if delta:
7665
        raise errors.OpPrereqError("Missing block device(s): %s" %
7666
                                   utils.CommaJoin(delta),
7667
                                   errors.ECODE_INVAL)
7668
      for dsk in self.disks:
7669
        dsk["size"] = int(float(node_disks[dsk["adopt"]]))
7670

  
7623 7671
    _CheckHVParams(self, nodenames, self.op.hypervisor, self.op.hvparams)
7624 7672

  
7625 7673
    _CheckNodeHasOS(self, pnode.name, self.op.os_type, self.op.force_variant)
......
7691 7739
                            )
7692 7740

  
7693 7741
    if self.adopt_disks:
7694
      # rename LVs to the newly-generated names; we need to construct
7695
      # 'fake' LV disks with the old data, plus the new unique_id
7696
      tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks]
7697
      rename_to = []
7698
      for t_dsk, a_dsk in zip (tmp_disks, self.disks):
7699
        rename_to.append(t_dsk.logical_id)
7700
        t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk["adopt"])
7701
        self.cfg.SetDiskID(t_dsk, pnode_name)
7702
      result = self.rpc.call_blockdev_rename(pnode_name,
7703
                                             zip(tmp_disks, rename_to))
7704
      result.Raise("Failed to rename adoped LVs")
7742
      if self.op.disk_template == constants.DT_PLAIN:
7743
        # rename LVs to the newly-generated names; we need to construct
7744
        # 'fake' LV disks with the old data, plus the new unique_id
7745
        tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks]
7746
        rename_to = []
7747
        for t_dsk, a_dsk in zip (tmp_disks, self.disks):
7748
          rename_to.append(t_dsk.logical_id)
7749
          t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk["adopt"])
7750
          self.cfg.SetDiskID(t_dsk, pnode_name)
7751
        result = self.rpc.call_blockdev_rename(pnode_name,
7752
                                               zip(tmp_disks, rename_to))
7753
        result.Raise("Failed to rename adoped LVs")
7705 7754
    else:
7706 7755
      feedback_fn("* creating instance disks...")
7707 7756
      try:
b/lib/constants.py
120 120
CRYPTO_KEYS_DIR_MODE = SECURE_DIR_MODE
121 121
IMPORT_EXPORT_DIR = RUN_GANETI_DIR + "/import-export"
122 122
IMPORT_EXPORT_DIR_MODE = 0755
123
ADOPTABLE_BLOCKDEV_ROOT = "/dev/disk/"
123 124
# keep RUN_GANETI_DIR first here, to make sure all get created when the node
124 125
# daemon is started (this takes care of RUN_DIR being tmpfs)
125 126
SUB_RUN_DIRS = [ RUN_GANETI_DIR, BDEV_CACHE_DIR, DISK_LINKS_DIR ]
......
363 364
DT_DRBD8 = "drbd"
364 365
DT_FILE = "file"
365 366
DT_SHARED_FILE = "sharedfile"
367
DT_BLOCK = "blockdev"
366 368

  
367 369
# the set of network-mirrored disk templates
368 370
DTS_NET_MIRROR = frozenset([DT_DRBD8])
369 371

  
370
# the set of externally mirrored disk templates
371
DTS_EXT_MIRROR = frozenset([DT_SHARED_FILE])
372
# the set of externally-mirrored disk templates (e.g. SAN, NAS)
373
DTS_EXT_MIRROR = frozenset([DT_SHARED_FILE, DT_BLOCK])
372 374

  
373 375
# the set of non-lvm-based disk templates
374
DTS_NOT_LVM = frozenset([DT_DISKLESS, DT_FILE, DT_SHARED_FILE])
376
DTS_NOT_LVM = frozenset([DT_DISKLESS, DT_FILE, DT_SHARED_FILE, DT_BLOCK])
375 377

  
376 378
# the set of disk templates which can be grown
377 379
DTS_GROWABLE = frozenset([DT_PLAIN, DT_DRBD8, DT_FILE, DT_SHARED_FILE])
378 380

  
379 381
# the set of disk templates that allow adoption
380
DTS_MAY_ADOPT = frozenset([DT_PLAIN])
382
DTS_MAY_ADOPT = frozenset([DT_PLAIN, DT_BLOCK])
383

  
384
# the set of disk templates that *must* use adoption
385
DTS_MUST_ADOPT = frozenset([DT_BLOCK])
381 386

  
382 387
# the set of disk templates that allow migrations
383 388
DTS_MIRRORED = frozenset.union(DTS_NET_MIRROR, DTS_EXT_MIRROR)
......
387 392
LD_LV = "lvm"
388 393
LD_DRBD8 = "drbd8"
389 394
LD_FILE = "file"
390
LDS_BLOCK = frozenset([LD_LV, LD_DRBD8])
395
LD_BLOCKDEV = "blockdev"
396
LDS_BLOCK = frozenset([LD_LV, LD_DRBD8, LD_BLOCKDEV])
391 397

  
392 398
# drbd constants
393 399
DRBD_HMAC_ALG = "md5"
......
460 466
CHILD_LINGER_TIMEOUT = 5.0
461 467

  
462 468
DISK_TEMPLATES = frozenset([DT_DISKLESS, DT_PLAIN, DT_DRBD8,
463
                            DT_FILE, DT_SHARED_FILE])
469
                            DT_FILE, DT_SHARED_FILE, DT_BLOCK])
464 470

  
465 471
FILE_DRIVER = frozenset([FD_LOOP, FD_BLKTAP])
466 472

  
......
1279 1285
  ALLOC_POLICY_LAST_RESORT,
1280 1286
  ALLOC_POLICY_UNALLOCABLE,
1281 1287
  ]
1288

  
1289
# Temporary external/shared storage parameters
1290
BLOCKDEV_DRIVER_MANUAL = "manual"
b/lib/objects.py
441 441
    """
442 442
    if self.dev_type == constants.LD_LV:
443 443
      return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
444
    elif self.dev_type == constants.LD_BLOCKDEV:
445
      return self.logical_id[1]
444 446
    return None
445 447

  
446 448
  def ChildrenNeeded(self):
......
483 485
    devices needs to (or can) be assembled.
484 486

  
485 487
    """
486
    if self.dev_type in [constants.LD_LV, constants.LD_FILE]:
488
    if self.dev_type in [constants.LD_LV, constants.LD_FILE,
489
                         constants.LD_BLOCKDEV]:
487 490
      result = [node]
488 491
    elif self.dev_type in constants.LDS_DRBD:
489 492
      result = [self.logical_id[0], self.logical_id[1]]

Also available in: Unified diff