Revision fffe93e7 lib/bdev.py

b/lib/bdev.py
2183 2183
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2184 2184

  
2185 2185

  
2186
class RADOSBlockDevice(BlockDev):
2187
  """A RADOS Block Device (rbd)
2188

  
2189
  This class implements the RADOS Block Device for the backend. You need
2190
  the rbd kernel driver, the RADOS Tools and a RADOS working cluster for
2191
  this to be functional.
2192

  
2193
  """
2194
  def __init__(self, unique_id, children, size):
2195
    """Attaches to an rbd device.
2196

  
2197
    """
2198
    super(RADOSBlockDevice, self).__init__(unique_id, children, size)
2199
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2200
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2201

  
2202
    self.unique_id = unique_id
2203

  
2204
    self.major = self.minor = None
2205
    self.Attach()
2206

  
2207
  @classmethod
2208
  def Create(cls, unique_id, children, size):
2209
    """Create a new rbd device
2210

  
2211
    Provision a new rbd image file inside a RADOS pool
2212

  
2213
    """
2214
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2215
      raise errors.ProgrammerError("Invalid configuration data %s" %
2216
                                   str(unique_id))
2217
    rbd_pool, rbd_name = unique_id
2218

  
2219
    # Provision a new rbd Volume (Image) inside the RADOS cluster
2220
    cmd = "rbd create -p %s %s --size %s" % (rbd_pool, rbd_name, size)
2221
    result = utils.RunCmd(cmd)
2222
    if result.failed:
2223
      _ThrowError("rbd creation failed (%s): %s",
2224
                  result.fail_reason, result.output)
2225

  
2226
    return RADOSBlockDevice(unique_id, children, size)
2227

  
2228
  def Remove(self):
2229
    """Remove the rbd device
2230

  
2231
    """
2232
    rbd_pool, rbd_name = self.unique_id
2233

  
2234
    if not self.minor and not self.Attach():
2235
      # the rbd device doesn't exist
2236
      return
2237

  
2238
    # First shutdown the device (remove mappings).
2239
    self.Shutdown()
2240

  
2241
    # Remove the actual Volume (Image) from the RADOS cluster
2242
    cmd = "rbd rm -p %s %s" % (rbd_pool, rbd_name)
2243
    result = utils.RunCmd(cmd)
2244
    if result.failed:
2245
      _ThrowError("Can't remove Volume from cluster with rbd rm: %s - %s",
2246
                  result.fail_reason, result.output)
2247

  
2248
  def Rename(self, new_id):
2249
    """Rename this device.
2250

  
2251
    """
2252
    pass
2253

  
2254
  def Attach(self):
2255
    """Attach to an existing rbd device.
2256

  
2257
    This method maps the rbd volume that matches our name with
2258
    an rbd device and then attaches to this device
2259

  
2260
    """
2261
    self.attached = False
2262

  
2263
    # Map the rbd Volume to a block device under /dev
2264
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
2265

  
2266
    try:
2267
      st = os.stat(self.dev_path)
2268
    except OSError, err:
2269
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2270
      return False
2271

  
2272
    if not stat.S_ISBLK(st.st_mode):
2273
      logging.error("%s is not a block device", self.dev_path)
2274
      return False
2275

  
2276
    self.major = os.major(st.st_rdev)
2277
    self.minor = os.minor(st.st_rdev)
2278
    self.attached = True
2279

  
2280
    return True
2281

  
2282
  def _MapVolumeToBlockdev(self, unique_id):
2283
    """Maps existing rbd Volumes (Images) to block devices
2284

  
2285
       This method should be idempotent if the mapping already exists.
2286
       If the mapping exists it returns the device path.
2287

  
2288
       If the mapping doesn't exist:
2289
       Maps the existing Volume (Image) named `name' (inside the RADOS
2290
       pool named `pool'), to a block device e.g. /dev/rbd{X}. Then
2291
       returns it's device path.
2292

  
2293
    """
2294
    pool, name = unique_id
2295

  
2296
    # Check if the mapping already exists
2297
    cmd1 = "rbd showmapped"
2298
    result = utils.RunCmd(cmd1)
2299
    if result.failed:
2300
      _ThrowError("rbd showmapped failed (%s): %s",
2301
                  result.fail_reason, result.output)
2302
    else:
2303
      cmd2 = "echo '%s' | grep %s | grep %s" % (result.output, pool, name)
2304
      result = utils.RunCmd(cmd2)
2305
      if not result.failed:
2306
        # The mapping already exists.
2307
        # Parse the result and return the rbd device
2308
        try:
2309
          rbd_dev = re.search("(/dev/rbd\d+)", result.output).group(1)
2310
        except:
2311
          # maybe we can add an assert here
2312
          _ThrowError("You shouldn't get here")
2313

  
2314
        return rbd_dev
2315

  
2316
      else:
2317
        # The mapping doesn't exist. Create it
2318
        cmd = "rbd map -p %s %s" % (pool, name)
2319
        result = utils.RunCmd(cmd)
2320
        if result.failed:
2321
          _ThrowError("rbd map failed (%s): %s",
2322
                      result.fail_reason, result.output)
2323
        # Use rbd showmapped again to find the rbd device
2324
        # the image was mapped to
2325
        cmd3 = "rbd showmapped | grep %s | grep %s" % (pool, name)
2326
        result = utils.RunCmd(cmd3)
2327
        if result.failed:
2328
          _ThrowError("Can't find mapped device. "
2329
                      "rbd showmapped failed (%s): %s",
2330
                      result.fail_reason, result.output)
2331
        try:
2332
          rbd_dev = re.search("(/dev/rbd\d+)", result.output).group(1)
2333
        except:
2334
          # maybe we can add an assert here
2335
          _ThrowError("You shouldn't get here")
2336

  
2337
        # The device was successfully mapped. Return it
2338
        return rbd_dev
2339

  
2340
  def Assemble(self):
2341
    """Assemble the device.
2342

  
2343
    """
2344
    pass
2345

  
2346
  def Shutdown(self):
2347
    """Shutdown the device.
2348

  
2349
    """
2350
    # Unmap the rbd device
2351
    # !doesn't unmap during migration because the
2352
    # !shutdown method is never called if the machine was up
2353
    # TODO: Fix cmdlib.py to shutdown the machine in the source node
2354
    # once the migration ends successfully
2355
    if not self.minor and not self.Attach():
2356
      # the rbd device doesn't exist
2357
      return
2358

  
2359
    # Unmap the block device from the Volume
2360
    self._UnmapVolumeFromBlockdev(self.unique_id)
2361

  
2362
    self.minor = None
2363
    self.dev_path = None
2364

  
2365
  def _UnmapVolumeFromBlockdev(self, unique_id):
2366
    """Unmaps the rbd device from the Volume it is mapped
2367

  
2368
    Unmaps the rbd device from the Volume (Image) it was
2369
    previously mapped to. This method should be idempotent if
2370
    the Volume isn't mapped.
2371

  
2372
    """
2373
    pool, name = unique_id
2374

  
2375
    # Check if the mapping already exists
2376
    cmd1 = "rbd showmapped"
2377
    result = utils.RunCmd(cmd1)
2378
    if result.failed:
2379
      _ThrowError("rbd showmapped failed [during unmap](%s): %s",
2380
                  result.fail_reason, result.output)
2381
    else:
2382
      cmd2 = "echo '%s' | grep %s | grep %s" % (result.output, pool, name)
2383
      result = utils.RunCmd(cmd2)
2384
      if not result.failed:
2385
        # The mapping already exists.
2386
        # Parse the result to find the rbd device
2387
        try:
2388
          rbd_dev = re.search("(/dev/rbd\d+)", result.output).group(1)
2389
        except:
2390
          # maybe we can add an assert here
2391
          _ThrowError("You shouldn't get here")
2392

  
2393
        # Unmap the rbd device
2394
        cmd = "rbd unmap %s" % rbd_dev
2395
        result = utils.RunCmd(cmd)
2396
        if result.failed:
2397
          _ThrowError("rbd unmap failed (%s): %s",
2398
                      result.fail_reason, result.output)
2399

  
2400
      else:
2401
        # The mapping doesn't exist. Do nothing
2402
        pass
2403

  
2404
  def Open(self, force=False):
2405
    """Make the device ready for I/O.
2406

  
2407
    """
2408
    pass
2409

  
2410
  def Close(self):
2411
    """Notifies that the device will no longer be used for I/O.
2412

  
2413
    """
2414
    pass
2415

  
2416
  def Grow(self, amount):
2417
    """Grow the volume.
2418

  
2419
    Grow the rbd Volume by `amount' megabytes.
2420
    Total block size will be: size + amount
2421

  
2422
    """
2423
    if not self.Attach():
2424
      _ThrowError("Can't attach to rbd device during Grow()")
2425

  
2426
    rbd_pool, rbd_name = self.unique_id
2427
    new_size = self.size + amount
2428

  
2429
    # Resize the rbd Volume (Image) inside the RADOS cluster
2430
    cmd = "rbd resize -p %s %s --size %s" % (rbd_pool, rbd_name, new_size)
2431
    result = utils.RunCmd(cmd)
2432
    if result.failed:
2433
      _ThrowError("rbd resize failed (%s): %s",
2434
                  result.fail_reason, result.output)
2435

  
2436

  
2186 2437
DEV_MAP = {
2187 2438
  constants.LD_LV: LogicalVolume,
2188 2439
  constants.LD_DRBD8: DRBD8,
2189 2440
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2441
  constants.LD_RBD: RADOSBlockDevice,
2190 2442
  }
2191 2443

  
2192 2444
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:

Also available in: Unified diff