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