Revision bdecfea2
b/lib/bdev.py | ||
---|---|---|
38 | 38 |
from ganeti import compat |
39 | 39 |
from ganeti import netutils |
40 | 40 |
from ganeti import pathutils |
41 |
from ganeti import serializer |
|
41 | 42 |
|
42 | 43 |
|
43 | 44 |
# Size of reads in _CanReadDevice |
44 | 45 |
_DEVICE_READ_SIZE = 128 * 1024 |
45 | 46 |
|
46 | 47 |
|
48 |
class RbdShowmappedJsonError(Exception): |
|
49 |
"""`rbd showmmapped' JSON formatting error Exception class. |
|
50 |
|
|
51 |
""" |
|
52 |
pass |
|
53 |
|
|
54 |
|
|
47 | 55 |
def _IgnoreError(fn, *args, **kwargs): |
48 | 56 |
"""Executes the given function, ignoring BlockDeviceErrors. |
49 | 57 |
|
... | ... | |
2726 | 2734 |
name = unique_id[1] |
2727 | 2735 |
|
2728 | 2736 |
# Check if the mapping already exists. |
2729 |
showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool] |
|
2730 |
result = utils.RunCmd(showmap_cmd) |
|
2731 |
if result.failed: |
|
2732 |
_ThrowError("rbd showmapped failed (%s): %s", |
|
2733 |
result.fail_reason, result.output) |
|
2734 |
|
|
2735 |
rbd_dev = self._ParseRbdShowmappedOutput(result.output, name) |
|
2736 |
|
|
2737 |
rbd_dev = self._VolumeToBlockdev(pool, name) |
|
2737 | 2738 |
if rbd_dev: |
2738 | 2739 |
# The mapping exists. Return it. |
2739 | 2740 |
return rbd_dev |
... | ... | |
2746 | 2747 |
result.fail_reason, result.output) |
2747 | 2748 |
|
2748 | 2749 |
# Find the corresponding rbd device. |
2749 |
showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool] |
|
2750 |
result = utils.RunCmd(showmap_cmd) |
|
2751 |
if result.failed: |
|
2752 |
_ThrowError("rbd map succeeded, but showmapped failed (%s): %s", |
|
2753 |
result.fail_reason, result.output) |
|
2754 |
|
|
2755 |
rbd_dev = self._ParseRbdShowmappedOutput(result.output, name) |
|
2756 |
|
|
2750 |
rbd_dev = self._VolumeToBlockdev(pool, name) |
|
2757 | 2751 |
if not rbd_dev: |
2758 | 2752 |
_ThrowError("rbd map succeeded, but could not find the rbd block" |
2759 | 2753 |
" device in output of showmapped, for volume: %s", name) |
... | ... | |
2761 | 2755 |
# The device was successfully mapped. Return it. |
2762 | 2756 |
return rbd_dev |
2763 | 2757 |
|
2758 |
@classmethod |
|
2759 |
def _VolumeToBlockdev(cls, pool, volume_name): |
|
2760 |
"""Do the 'volume name'-to-'rbd block device' resolving. |
|
2761 |
|
|
2762 |
@type pool: string |
|
2763 |
@param pool: RADOS pool to use |
|
2764 |
@type volume_name: string |
|
2765 |
@param volume_name: the name of the volume whose device we search for |
|
2766 |
@rtype: string or None |
|
2767 |
@return: block device path if the volume is mapped, else None |
|
2768 |
|
|
2769 |
""" |
|
2770 |
try: |
|
2771 |
# Newer versions of the rbd tool support json output formatting. Use it |
|
2772 |
# if available. |
|
2773 |
showmap_cmd = [ |
|
2774 |
constants.RBD_CMD, |
|
2775 |
"showmapped", |
|
2776 |
"-p", |
|
2777 |
pool, |
|
2778 |
"--format", |
|
2779 |
"json" |
|
2780 |
] |
|
2781 |
result = utils.RunCmd(showmap_cmd) |
|
2782 |
if result.failed: |
|
2783 |
logging.error("rbd JSON output formatting returned error (%s): %s," |
|
2784 |
"falling back to plain output parsing", |
|
2785 |
result.fail_reason, result.output) |
|
2786 |
raise RbdShowmappedJsonError |
|
2787 |
|
|
2788 |
return cls._ParseRbdShowmappedJson(result.output, volume_name) |
|
2789 |
except RbdShowmappedJsonError: |
|
2790 |
# For older versions of rbd, we have to parse the plain / text output |
|
2791 |
# manually. |
|
2792 |
showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool] |
|
2793 |
result = utils.RunCmd(showmap_cmd) |
|
2794 |
if result.failed: |
|
2795 |
_ThrowError("rbd showmapped failed (%s): %s", |
|
2796 |
result.fail_reason, result.output) |
|
2797 |
|
|
2798 |
return cls._ParseRbdShowmappedPlain(result.output, volume_name) |
|
2799 |
|
|
2800 |
@staticmethod |
|
2801 |
def _ParseRbdShowmappedJson(output, volume_name): |
|
2802 |
"""Parse the json output of `rbd showmapped'. |
|
2803 |
|
|
2804 |
This method parses the json output of `rbd showmapped' and returns the rbd |
|
2805 |
block device path (e.g. /dev/rbd0) that matches the given rbd volume. |
|
2806 |
|
|
2807 |
@type output: string |
|
2808 |
@param output: the json output of `rbd showmapped' |
|
2809 |
@type volume_name: string |
|
2810 |
@param volume_name: the name of the volume whose device we search for |
|
2811 |
@rtype: string or None |
|
2812 |
@return: block device path if the volume is mapped, else None |
|
2813 |
|
|
2814 |
""" |
|
2815 |
try: |
|
2816 |
devices = serializer.LoadJson(output) |
|
2817 |
except ValueError, err: |
|
2818 |
_ThrowError("Unable to parse JSON data: %s" % err) |
|
2819 |
|
|
2820 |
rbd_dev = None |
|
2821 |
for d in devices.values(): # pylint: disable=E1103 |
|
2822 |
try: |
|
2823 |
name = d["name"] |
|
2824 |
except KeyError: |
|
2825 |
_ThrowError("'name' key missing from json object %s", devices) |
|
2826 |
|
|
2827 |
if name == volume_name: |
|
2828 |
if rbd_dev is not None: |
|
2829 |
_ThrowError("rbd volume %s is mapped more than once", volume_name) |
|
2830 |
|
|
2831 |
rbd_dev = d["device"] |
|
2832 |
|
|
2833 |
return rbd_dev |
|
2834 |
|
|
2764 | 2835 |
@staticmethod |
2765 |
def _ParseRbdShowmappedOutput(output, volume_name):
|
|
2766 |
"""Parse the output of `rbd showmapped'. |
|
2836 |
def _ParseRbdShowmappedPlain(output, volume_name):
|
|
2837 |
"""Parse the (plain / text) output of `rbd showmapped'.
|
|
2767 | 2838 |
|
2768 | 2839 |
This method parses the output of `rbd showmapped' and returns |
2769 | 2840 |
the rbd block device path (e.g. /dev/rbd0) that matches the |
2770 | 2841 |
given rbd volume. |
2771 | 2842 |
|
2772 | 2843 |
@type output: string |
2773 |
@param output: the whole output of `rbd showmapped'
|
|
2844 |
@param output: the plain text output of `rbd showmapped'
|
|
2774 | 2845 |
@type volume_name: string |
2775 | 2846 |
@param volume_name: the name of the volume whose device we search for |
2776 | 2847 |
@rtype: string or None |
... | ... | |
2781 | 2852 |
volumefield = 2 |
2782 | 2853 |
devicefield = 4 |
2783 | 2854 |
|
2784 |
field_sep = "\t" |
|
2785 |
|
|
2786 | 2855 |
lines = output.splitlines() |
2787 |
splitted_lines = map(lambda l: l.split(field_sep), lines) |
|
2788 | 2856 |
|
2789 |
# Check empty output. |
|
2857 |
# Try parsing the new output format (ceph >= 0.55). |
|
2858 |
splitted_lines = map(lambda l: l.split(), lines) |
|
2859 |
|
|
2860 |
# Check for empty output. |
|
2790 | 2861 |
if not splitted_lines: |
2791 |
_ThrowError("rbd showmapped returned empty output")
|
|
2862 |
return None
|
|
2792 | 2863 |
|
2793 |
# Check showmapped header line, to determine number of fields.
|
|
2864 |
# Check showmapped output, to determine number of fields.
|
|
2794 | 2865 |
field_cnt = len(splitted_lines[0]) |
2795 | 2866 |
if field_cnt != allfields: |
2796 |
_ThrowError("Cannot parse rbd showmapped output because its format" |
|
2797 |
" seems to have changed; expected %s fields, found %s", |
|
2798 |
allfields, field_cnt) |
|
2867 |
# Parsing the new format failed. Fallback to parsing the old output |
|
2868 |
# format (< 0.55). |
|
2869 |
splitted_lines = map(lambda l: l.split("\t"), lines) |
|
2870 |
if field_cnt != allfields: |
|
2871 |
_ThrowError("Cannot parse rbd showmapped output expected %s fields," |
|
2872 |
" found %s", allfields, field_cnt) |
|
2799 | 2873 |
|
2800 | 2874 |
matched_lines = \ |
2801 | 2875 |
filter(lambda l: len(l) == allfields and l[volumefield] == volume_name, |
2802 | 2876 |
splitted_lines) |
2803 | 2877 |
|
2804 | 2878 |
if len(matched_lines) > 1: |
2805 |
_ThrowError("The rbd volume %s is mapped more than once." |
|
2806 |
" This shouldn't happen, try to unmap the extra" |
|
2807 |
" devices manually.", volume_name) |
|
2879 |
_ThrowError("rbd volume %s mapped more than once", volume_name) |
|
2808 | 2880 |
|
2809 | 2881 |
if matched_lines: |
2810 | 2882 |
# rbd block device found. Return it. |
... | ... | |
2845 | 2917 |
name = unique_id[1] |
2846 | 2918 |
|
2847 | 2919 |
# Check if the mapping already exists. |
2848 |
showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool] |
|
2849 |
result = utils.RunCmd(showmap_cmd) |
|
2850 |
if result.failed: |
|
2851 |
_ThrowError("rbd showmapped failed [during unmap](%s): %s", |
|
2852 |
result.fail_reason, result.output) |
|
2853 |
|
|
2854 |
rbd_dev = self._ParseRbdShowmappedOutput(result.output, name) |
|
2920 |
rbd_dev = self._VolumeToBlockdev(pool, name) |
|
2855 | 2921 |
|
2856 | 2922 |
if rbd_dev: |
2857 | 2923 |
# The mapping exists. Unmap the rbd device. |
Also available in: Unified diff