]>
Commit | Line | Data |
---|---|---|
3efd9988 FG |
1 | import os |
2 | import stat | |
181888fb FG |
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 | ['sudo', '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 | ['sudo', 'blkid', '-t', 'PARTUUID="%s"' % partuuid, '-o', 'device'] | |
25 | ) | |
26 | return ' '.join(out).strip() | |
3efd9988 FG |
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 | $ sudo 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 = ['sudo', '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 | ['sudo', '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 |