initial code repo
[stor4nfv.git] / src / ceph / src / ceph-volume / ceph_volume / util / disk.py
1 import os
2 import stat
3 from ceph_volume import process
4
5
6 def get_partuuid(device):
7     """
8     If a device is a partition, it will probably have a PARTUUID on it that
9     will persist and can be queried against `blkid` later to detect the actual
10     device
11     """
12     out, err, rc = process.call(
13         ['blkid', '-s', 'PARTUUID', '-o', 'value', device]
14     )
15     return ' '.join(out).strip()
16
17
18 def get_device_from_partuuid(partuuid):
19     """
20     If a device has a partuuid, query blkid so that it can tell us what that
21     device is
22     """
23     out, err, rc = process.call(
24         ['blkid', '-t', 'PARTUUID="%s"' % partuuid, '-o', 'device']
25     )
26     return ' '.join(out).strip()
27
28
29 def _stat_is_device(stat_obj):
30     """
31     Helper function that will interpret ``os.stat`` output directly, so that other
32     functions can call ``os.stat`` once and interpret that result several times
33     """
34     return stat.S_ISBLK(stat_obj)
35
36
37 def lsblk(device, columns=None):
38     """
39     Create a dictionary of identifying values for a device using ``lsblk``.
40     Each supported column is a key, in its *raw* format (all uppercase
41     usually).  ``lsblk`` has support for certain "columns" (in blkid these
42     would be labels), and these columns vary between distributions and
43     ``lsblk`` versions. The newer versions support a richer set of columns,
44     while older ones were a bit limited.
45
46     These are the default lsblk columns reported which are safe to use for
47     Ubuntu 14.04.5 LTS:
48
49          NAME  device name
50         KNAME  internal kernel device name
51       MAJ:MIN  major:minor device number
52        FSTYPE  filesystem type
53    MOUNTPOINT  where the device is mounted
54         LABEL  filesystem LABEL
55          UUID  filesystem UUID
56            RO  read-only device
57            RM  removable device
58         MODEL  device identifier
59          SIZE  size of the device
60         STATE  state of the device
61         OWNER  user name
62         GROUP  group name
63          MODE  device node permissions
64     ALIGNMENT  alignment offset
65        MIN-IO  minimum I/O size
66        OPT-IO  optimal I/O size
67       PHY-SEC  physical sector size
68       LOG-SEC  logical sector size
69          ROTA  rotational device
70         SCHED  I/O scheduler name
71       RQ-SIZE  request queue size
72          TYPE  device type
73      DISC-ALN  discard alignment offset
74     DISC-GRAN  discard granularity
75      DISC-MAX  discard max bytes
76     DISC-ZERO  discard zeroes data
77
78     There is a bug in ``lsblk`` where using all the available (supported)
79     columns will result in no output (!), in order to workaround this the
80     following columns have been removed from the default reporting columns:
81
82     * RQ-SIZE (request queue size)
83     * MIN-IO  minimum I/O size
84     * OPT-IO  optimal I/O size
85
86     These should be available however when using `columns`. For example::
87
88         >>> lsblk('/dev/sda1', columns=['OPT-IO'])
89         {'OPT-IO': '0'}
90
91     Normal CLI output, as filtered by the flags in this function will look like ::
92
93         $ lsblk --nodeps -P -o NAME,KNAME,MAJ:MIN,FSTYPE,MOUNTPOINT
94         NAME="sda1" KNAME="sda1" MAJ:MIN="8:1" FSTYPE="ext4" MOUNTPOINT="/"
95
96     :param columns: A list of columns to report as keys in its original form.
97     """
98     default_columns = [
99         'NAME', 'KNAME', 'MAJ:MIN', 'FSTYPE', 'MOUNTPOINT', 'LABEL', 'UUID',
100         'RO', 'RM', 'MODEL', 'SIZE', 'STATE', 'OWNER', 'GROUP', 'MODE',
101         'ALIGNMENT', 'PHY-SEC', 'LOG-SEC', 'ROTA', 'SCHED', 'TYPE', 'DISC-ALN',
102         'DISC-GRAN', 'DISC-MAX', 'DISC-ZERO'
103     ]
104     device = device.rstrip('/')
105     columns = columns or default_columns
106     # --nodeps -> Avoid adding children/parents to the device, only give information
107     #             on the actual device we are querying for
108     # -P       -> Produce pairs of COLUMN="value"
109     # -o       -> Use the columns specified or default ones provided by this function
110     command = ['lsblk', '--nodeps', '-P', '-o']
111     command.append(','.join(columns))
112     command.append(device)
113     out, err, rc = process.call(command)
114
115     if rc != 0:
116         return {}
117
118     # parse the COLUMN="value" output to construct the dictionary
119     pairs = ' '.join(out).split()
120     parsed = {}
121     for pair in pairs:
122         try:
123             column, value = pair.split('=')
124         except ValueError:
125             continue
126         parsed[column] = value.strip().strip().strip('"')
127     return parsed
128
129
130 def _lsblk_type(device):
131     """
132     Helper function that will use the ``TYPE`` label output of ``lsblk`` to determine
133     if a device is a partition or disk.
134     It does not process the output to return a boolean, but it does process it to return the
135     """
136     out, err, rc = process.call(
137         ['blkid', '-s', 'PARTUUID', '-o', 'value', device]
138     )
139     return ' '.join(out).strip()
140
141
142 def is_device(dev):
143     """
144     Boolean to determine if a given device is a block device (**not**
145     a partition!)
146
147     For example: /dev/sda would return True, but not /dev/sdc1
148     """
149     if not os.path.exists(dev):
150         return False
151     # use lsblk first, fall back to using stat
152     TYPE = lsblk(dev).get('TYPE')
153     if TYPE:
154         return TYPE == 'disk'
155
156     # fallback to stat
157     return _stat_is_device(os.lstat(dev).st_mode)
158     if stat.S_ISBLK(os.lstat(dev)):
159         return True
160     return False
161
162
163 def is_partition(dev):
164     """
165     Boolean to determine if a given device is a partition, like /dev/sda1
166     """
167     if not os.path.exists(dev):
168         return False
169     # use lsblk first, fall back to using stat
170     TYPE = lsblk(dev).get('TYPE')
171     if TYPE:
172         return TYPE == 'part'
173
174     # fallback to stat
175     stat_obj = os.stat(dev)
176     if _stat_is_device(stat_obj.st_mode):
177         return False
178
179     major = os.major(stat_obj.st_rdev)
180     minor = os.minor(stat_obj.st_rdev)
181     if os.path.exists('/sys/dev/block/%d:%d/partition' % (major, minor)):
182         return True
183     return False