Merge ~mwhudson/curtin:pare-down-dasdview-parsing into curtin:master
- Git
- lp:~mwhudson/curtin
- pare-down-dasdview-parsing
- Merge into master
Status: | Merged |
---|---|
Approved by: | Paride Legovini |
Approved revision: | e9b3d0140d05f5a3f291ec99bd0d10d81969e4be |
Merge reported by: | Server Team CI bot |
Merged at revision: | not available |
Proposed branch: | ~mwhudson/curtin:pare-down-dasdview-parsing |
Merge into: | curtin:master |
Diff against target: |
717 lines (+116/-400) 2 files modified
curtin/block/dasd.py (+43/-232) tests/unittests/test_block_dasd.py (+73/-168) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Server Team CI bot | continuous-integration | Approve | |
Paride Legovini | Approve | ||
Review via email: mp+394207@code.launchpad.net |
Commit message
simplify dasdview parsing code
Now that the only thing we need dasdview output for is to determine
the format type of the dasd (ldl vs cdl vs unformatted) we can make
a few things simpler.
Description of the change
Server Team CI bot (server-team-bot) wrote : | # |
Paride Legovini (paride) wrote : | # |
Jenkins lost connection with the worker node. Retriggering.
Caused: java.io.
at hudson.
at hudson.
at com.sun.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
at hudson.
Ryan Harper (raharper) : | # |
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:b070e5b7032
https:/
Executed test runs:
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Dimitri John Ledkov (xnox) : | # |
Paride Legovini (paride) wrote : | # |
The arm64 Jenkins agent disconnected from the Jenkins master, and wouldn't reconnect.
While investigating I found out that after the data center move the arm64 node was pointing to a NTP server which became inaccessible, its clock went out of sync (several hours) and I *think* it was kicked from the worker nodes pool for this reason. Now it's online again, with a good NTP. I restarted the job.
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:b070e5b7032
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Michael Hudson-Doyle (mwhudson) : | # |
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:e9b3d0140d0
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Paride Legovini (paride) wrote : | # |
This LGTM from the code perspective, but I know very little about DASD.
Server Team CI bot (server-team-bot) wrote : | # |
Commit message lints:
- Line #2 has 83 too many characters. Line starts with: "Now that the only thing"...
Server Team CI bot (server-team-bot) : | # |
Preview Diff
1 | diff --git a/curtin/block/dasd.py b/curtin/block/dasd.py |
2 | index 7450300..0529c25 100644 |
3 | --- a/curtin/block/dasd.py |
4 | +++ b/curtin/block/dasd.py |
5 | @@ -1,13 +1,12 @@ |
6 | # This file is part of curtin. See LICENSE file for copyright and license info. |
7 | |
8 | -import collections |
9 | +import glob |
10 | import os |
11 | +import re |
12 | import tempfile |
13 | from curtin import util |
14 | from curtin.log import LOG, logged_time |
15 | |
16 | -Dasdvalue = collections.namedtuple('Dasdvalue', ['hex', 'dec', 'txt']) |
17 | - |
18 | |
19 | class DasdPartition: |
20 | def __init__(self, table, device, start, end, length, id, system): |
21 | @@ -116,196 +115,43 @@ def dasdinfo(device_id): |
22 | return util.load_shell_content(out) |
23 | |
24 | |
25 | -def dasdview(devname): |
26 | - ''' Run dasdview on devname and return dictionary of data. |
27 | - |
28 | - dasdview --extended has 3 sections |
29 | - general (2:6), geometry (8:12), extended (14:) |
30 | - |
31 | - ''' |
32 | +def dasd_format(devname): |
33 | + """Return the format (ldl/cdl/not-formatted) of devname.""" |
34 | if not os.path.exists(devname): |
35 | raise ValueError("Invalid dasd device name: '%s'" % devname) |
36 | |
37 | out, err = util.subp(['dasdview', '--extended', devname], capture=True) |
38 | |
39 | - return _parse_dasdview(out) |
40 | - |
41 | - |
42 | -def _parse_dasdview(view_output): |
43 | - """ Parse dasdview --extended output into a dictionary |
44 | - |
45 | - Input: |
46 | - --- general DASD information --------------------------------------------- |
47 | - device node : /dev/dasdd |
48 | - busid : 0.0.1518 |
49 | - type : ECKD |
50 | - device type : hex 3390 dec 13200 |
51 | - |
52 | - --- DASD geometry -------------------------------------------------------- |
53 | - number of cylinders : hex 2721 dec 10017 |
54 | - tracks per cylinder : hex f dec 15 |
55 | - blocks per track : hex c dec 12 |
56 | - blocksize : hex 1000 dec 4096 |
57 | - |
58 | - --- extended DASD information -------------------------------------------- |
59 | - real device number : hex 0 dec 0 |
60 | - subchannel identifier : hex 178 dec 376 |
61 | - CU type (SenseID) : hex 3990 dec 14736 |
62 | - CU model (SenseID) : hex e9 dec 233 |
63 | - device type (SenseID) : hex 3390 dec 13200 |
64 | - device model (SenseID) : hex c dec 12 |
65 | - open count : hex 1 dec 1 |
66 | - req_queue_len : hex 0 dec 0 |
67 | - chanq_len : hex 0 dec 0 |
68 | - status : hex 5 dec 5 |
69 | - label_block : hex 2 dec 2 |
70 | - FBA_layout : hex 0 dec 0 |
71 | - characteristics_size : hex 40 dec 64 |
72 | - confdata_size : hex 100 dec 256 |
73 | - format : hex 2 dec 2 CDL formatted |
74 | - features : hex 0 dec 0 default |
75 | - |
76 | - characteristics : 3990e933 900c5e0c 39f72032 2721000f |
77 | - e000e5a2 05940222 13090674 00000000 |
78 | - 00000000 00000000 32321502 dfee0001 |
79 | - 0677080f 007f4800 1f3c0000 00002721 |
80 | - |
81 | - configuration_data : dc010100 f0f0f2f1 f0f7f9f0 f0c9c2d4 |
82 | - f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10818 |
83 | - d4020000 f0f0f2f1 f0f7f9f6 f1c9c2d4 |
84 | - f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10800 |
85 | - d0000000 f0f0f2f1 f0f7f9f6 f1c9c2d4 |
86 | - f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f00800 |
87 | - f0000001 f0f0f2f1 f0f7f9f0 f0c9c2d4 |
88 | - f7f5f0f0 f0f0f0f0 f0c4e7d7 f7f10800 |
89 | - 00000000 00000000 00000000 00000000 |
90 | - 00000000 00000000 00000000 00000000 |
91 | - 00000000 00000000 00000000 00000000 |
92 | - 00000000 00000000 00000000 00000000 |
93 | - 00000000 00000000 00000000 00000000 |
94 | - 00000000 00000000 00000000 00000000 |
95 | - 81000003 2d001e00 15000247 000c0016 |
96 | - 000cc018 935e41ee 00030000 0000a000 |
97 | - |
98 | - Output: |
99 | - |
100 | - view = { |
101 | - 'extended': { |
102 | - 'chanq_len': Dasdvalue(hex='0x0', dec=0, txt=None), |
103 | - 'characteristics': ['3990e933', ...], # shortened for brevity |
104 | - 'characteristics_size': Dasdvalue(hex='0x40', dec=64, txt=None), |
105 | - 'confdata_size': Dasdvalue(hex='0x100', dec=256, txt=None), |
106 | - 'configuration_data': ['dc010100', ...], # shortened for brevity |
107 | - 'cu_model_senseid': Dasdvalue(hex='0xe9', dec=233, txt=None), |
108 | - 'cu_type__senseid': Dasdvalue(hex='0x3990', dec=14736, txt=None), |
109 | - 'device_model_senseid': Dasdvalue(hex='0xc', dec=12, txt=None), |
110 | - 'device_type__senseid': Dasdvalue(hex='0x3390', dec=13200, txt=None), |
111 | - 'fba_layout': Dasdvalue(hex='0x0', dec=0, txt=None), |
112 | - 'features': Dasdvalue(hex='0x0', dec=0, txt='default'), |
113 | - 'format': Dasdvalue(hex='0x2', dec=2, txt='cdl'), |
114 | - 'label_block': Dasdvalue(hex='0x2', dec=2, txt=None), |
115 | - 'open_count': Dasdvalue(hex='0x1', dec=1, txt=None), |
116 | - 'real_device_number': Dasdvalue(hex='0x0', dec=0, txt=None), |
117 | - 'req_queue_len': Dasdvalue(hex='0x0', dec=0, txt=None), |
118 | - 'status': Dasdvalue(hex='0x5', dec=5, txt=None), |
119 | - 'subchannel_identifier': Dasdvalue(hex='0x178', dec=376, txt=None)}, |
120 | - 'general': { |
121 | - 'busid': ['0.0.1518'], |
122 | - 'device_node': ['/dev/dasdd'], |
123 | - 'device_type': Dasdvalue(hex='0x3390', dec=13200, txt=None), |
124 | - 'type': ['ECKD']}, |
125 | - 'geometry': { |
126 | - 'blocks_per_track': Dasdvalue(hex='0xc', dec=12, txt=None), |
127 | - 'blocksize': Dasdvalue(hex='0x1000', dec=4096, txt=None), |
128 | - 'number_of_cylinders': Dasdvalue(hex='0x2721', dec=10017, txt=None), |
129 | - 'tracks_per_cylinder': Dasdvalue(hex='0xf', dec=15, txt=None)} |
130 | - } |
131 | - """ |
132 | + return _dasd_format(out) |
133 | |
134 | - info_key_map = { |
135 | - 'CDL formatted': 'cdl', |
136 | - 'LDL formatted': 'ldl', |
137 | - 'NOT formatted': 'not-formatted', |
138 | - } |
139 | |
140 | - def _mkdasdvalue(hex_str, dec_str, comment=None): |
141 | - v_hex = hex_str.replace('hex ', '0x') |
142 | - v_dec = int(dec_str.replace('dec ', '')) |
143 | - v_str = None |
144 | - if comment is not None: |
145 | - v_str = info_key_map.get(comment, comment) |
146 | - |
147 | - return Dasdvalue(v_hex, v_dec, v_str) |
148 | - |
149 | - def _map_strip(value): |
150 | - v_type = type(value) |
151 | - return v_type(map(lambda x: x.strip(), value)) |
152 | - |
153 | - def _parse_output(output): |
154 | - parsed = {} |
155 | - key = prev_key = value = None |
156 | - for line in output: |
157 | - if not line: |
158 | - continue |
159 | - if ':' in line: |
160 | - key, value = map(lambda x: x.strip(), |
161 | - line.lstrip().split(':')) |
162 | - # normalize lvalue |
163 | - key = key.replace(' ', '_') |
164 | - key = key.replace(' ', '_') |
165 | - key = key.replace('(', '').replace(')', '') |
166 | - key = key.lower() |
167 | - prev_key = key |
168 | - if value and '\t' in value: |
169 | - value = _map_strip(value.split('\t')) |
170 | - # [hex X, dec Y] |
171 | - # [hex X, dec Y, string] |
172 | - value = _mkdasdvalue(*value) |
173 | - elif value and ' ' in value: |
174 | - # characteristics : XXXXXXX XXXXXXX XXXXXXXX XXXXXXX |
175 | - # YYYYYYY YYYYYYY YYYYYYYY YYYYYYY |
176 | - # convert to list of strings. |
177 | - value = value.lstrip().split() |
178 | - else: |
179 | - value = None |
180 | - else: |
181 | - key = prev_key |
182 | - # no colon line, parse value from line |
183 | - # YYYYYYY YYYYYYY YYYYYYYY YYYYYYY |
184 | - value = line.lstrip().split() |
185 | - |
186 | - # extend lists for existing keys |
187 | - if key in parsed and type(value) == list: |
188 | - parsed[key].extend(value) |
189 | - else: |
190 | - parsed.update({key: value}) |
191 | - |
192 | - return parsed |
193 | - |
194 | - if not view_output or not isinstance(view_output, util.string_types): |
195 | - raise ValueError( |
196 | - 'Invalid value for input to parse: ' + str(view_output)) |
197 | +DASD_FORMAT = r"^format\s+:.+\s+(?P<value>\w+\s\w+)$" |
198 | + |
199 | + |
200 | +def find_val(regex, content): |
201 | + m = re.search(regex, content, re.MULTILINE) |
202 | + if m is not None: |
203 | + return m.group("value") |
204 | |
205 | - # XXX: dasdview --extended has 52 lines for dasd devices |
206 | - if len(view_output.splitlines()) < 52: |
207 | - raise ValueError( |
208 | - 'dasdview output has fewer than 52 lines, cannot parse') |
209 | |
210 | - lines = view_output.splitlines() |
211 | - gen_start, gen_end = (2, 6) |
212 | - geo_start, geo_end = (8, 12) |
213 | - ext_start, ext_end = (14, len(lines)) |
214 | +def _dasd_format(dasdview_output): |
215 | + """ Read and return specified device "disk_layout" value. |
216 | |
217 | - general_output = lines[gen_start:gen_end] |
218 | - geometry_output = lines[geo_start:geo_end] |
219 | - extended_output = lines[ext_start:ext_end] |
220 | + :returns: string: One of ['cdl', 'ldl', 'not-formatted']. |
221 | + :raises: ValueError if dasdview result missing 'format' section. |
222 | |
223 | - view = { |
224 | - 'general': _parse_output(general_output), |
225 | - 'geometry': _parse_output(geometry_output), |
226 | - 'extended': _parse_output(extended_output), |
227 | + """ |
228 | + if not dasdview_output: |
229 | + return |
230 | + |
231 | + mapping = { |
232 | + 'cdl formatted': 'cdl', |
233 | + 'ldl formatted': 'ldl', |
234 | + 'not formatted': 'not-formatted', |
235 | } |
236 | - return view |
237 | + diskfmt = find_val(DASD_FORMAT, dasdview_output) |
238 | + if diskfmt is not None: |
239 | + return mapping.get(diskfmt.lower()) |
240 | |
241 | |
242 | def _valid_device_id(device_id): |
243 | @@ -371,38 +217,9 @@ class CcwDevice(object): |
244 | |
245 | class DasdDevice(CcwDevice): |
246 | |
247 | - def __init__(self, device_id): |
248 | - super(DasdDevice, self).__init__(device_id) |
249 | - self._kname = None |
250 | - |
251 | - @property |
252 | - def kname(self): |
253 | - if not self._kname: |
254 | - self._kname = self._get_kname() |
255 | - return self._kname |
256 | - |
257 | - def _get_kname(self): |
258 | - """ Return the kernel name of the dasd device. """ |
259 | - if not self.is_online(): |
260 | - raise RuntimeError('Cannot determine dasd kname for offline ' |
261 | - 'device: %s' % self.device_id) |
262 | - |
263 | - blockdir = self.ccw_device_attr_path('block') |
264 | - if not os.path.isdir(blockdir): |
265 | - raise RuntimeError('Unexpectedly not a directory: %s' % blockdir) |
266 | - |
267 | - try: |
268 | - knames = os.listdir(blockdir) |
269 | - [dasd_kname] = knames |
270 | - except ValueError: |
271 | - raise RuntimeError('Unexpected os.listdir result at sysfs path ' |
272 | - '%s: "%s"' % (blockdir, knames)) |
273 | - |
274 | - return dasd_kname |
275 | - |
276 | @property |
277 | def devname(self): |
278 | - return '/dev/%s' % self.kname |
279 | + return '/dev/disk/by-path/ccw-%s' % self.device_id |
280 | |
281 | def partition(self, partnumber, partsize, strict=True): |
282 | """ Add a partition to this DasdDevice specifying partnumber and size. |
283 | @@ -461,42 +278,36 @@ class DasdDevice(CcwDevice): |
284 | """ Returns a boolean indicating if the specified device_id is not yet |
285 | formatted. |
286 | |
287 | - :param device_id: string of device ccw bus_id. |
288 | :returns: boolean: True if the device is not formatted. |
289 | """ |
290 | return self.ccw_device_attr('status') == "unformatted" |
291 | |
292 | - def is_online(self): |
293 | - """ Returns a boolean indicating if specified device is online. |
294 | - |
295 | - :param device_id: string of device ccw bus_id. |
296 | - :returns: boolean: True if device is online. |
297 | - """ |
298 | - return self.ccw_device_attr('online') == "1" |
299 | - |
300 | def blocksize(self): |
301 | """ Read and return device_id's 'blocksize' value. |
302 | |
303 | :param: device_id: string of device ccw bus_id. |
304 | :returns: string: the device's current blocksize. |
305 | """ |
306 | - blkattr = 'block/%s/queue/hw_sector_size' % self.kname |
307 | - return self.ccw_device_attr(blkattr) |
308 | + blkattr = 'block/*/queue/hw_sector_size' |
309 | + # In practice there will only be one entry in the directory |
310 | + # /sys/bus/ccw/devices/{device_id}/block/, but in case |
311 | + # something strange happens and there are more, this assumes |
312 | + # all block devices connected to the dasd have the same block |
313 | + # size... |
314 | + path = glob.glob(self.ccw_device_attr_path(blkattr))[0] |
315 | + return util.load_file(path) |
316 | |
317 | def disk_layout(self): |
318 | """ Read and return specified device "disk_layout" value. |
319 | |
320 | :returns: string: One of ['cdl', 'ldl', 'not-formatted']. |
321 | :raises: ValueError if dasdview result missing 'format' section. |
322 | - |
323 | """ |
324 | - view = dasdview(self.devname) |
325 | - disk_format = view.get('extended', {}).get('format') |
326 | - if not disk_format: |
327 | + format = dasd_format(self.devname) |
328 | + if not format: |
329 | raise ValueError( |
330 | - 'dasdview on %s missing "format" section' % self.devname) |
331 | - |
332 | - return disk_format.txt |
333 | + 'could not determine format of %s' % self.devname) |
334 | + return format |
335 | |
336 | def label(self): |
337 | """Read and return specified device label (VOLSER) value. |
338 | @@ -546,7 +357,7 @@ class DasdDevice(CcwDevice): |
339 | |
340 | @logged_time("DASD.FORMAT") |
341 | def format(self, blksize=4096, layout='cdl', force=False, set_label=None, |
342 | - keep_label=False, no_label=False, mode='quick', strict=True): |
343 | + keep_label=False, no_label=False, mode='quick'): |
344 | """ Format DasdDevice with supplied parameters. |
345 | |
346 | :param blksize: integer value to configure disk block size in bytes. |
347 | @@ -572,7 +383,7 @@ class DasdDevice(CcwDevice): |
348 | :param strict: boolean which enforces that dasd device exists before |
349 | issuing format command, defaults to True. |
350 | |
351 | - :raises: RuntimeError if strict==True and devname does not exist. |
352 | + :raises: RuntimeError if devname does not exist. |
353 | :raises: ValueError on invalid blocksize, disk_layout and mode. |
354 | :raises: ProcessExecutionError on errors running 'dasdfmt' command. |
355 | |
356 | @@ -580,7 +391,7 @@ class DasdDevice(CcwDevice): |
357 | dasdformat -y --blocksize=4096 --disk_layout=cdl \ |
358 | --mode=quick /dev/dasda |
359 | """ |
360 | - if strict and not os.path.exists(self.devname): |
361 | + if not os.path.exists(self.devname): |
362 | raise RuntimeError("devname '%s' does not exist" % self.devname) |
363 | |
364 | if no_label: |
365 | diff --git a/tests/unittests/test_block_dasd.py b/tests/unittests/test_block_dasd.py |
366 | index dfd62d3..fffa4a1 100644 |
367 | --- a/tests/unittests/test_block_dasd.py |
368 | +++ b/tests/unittests/test_block_dasd.py |
369 | @@ -85,80 +85,18 @@ class TestDasdValidDeviceId(CiTestCase): |
370 | dasd.DasdDevice(invalid_dev) |
371 | |
372 | |
373 | -class TestDasdDeviceIdToKname(CiTestCase): |
374 | - |
375 | - def setUp(self): |
376 | - super(TestDasdDeviceIdToKname, self).setUp() |
377 | - self.add_patch('curtin.block.dasd.os.path.isdir', 'm_isdir') |
378 | - self.add_patch('curtin.block.dasd.os.listdir', 'm_listdir') |
379 | - self.add_patch('curtin.block.dasd.DasdDevice.is_online', 'm_online') |
380 | - |
381 | - self.dasd = dasd.DasdDevice(random_device_id()) |
382 | - self.m_online.return_value = True |
383 | - self.m_isdir.return_value = True |
384 | - self.m_listdir.return_value = [self.random_string()] |
385 | - |
386 | - def test_device_id_to_kname_returns_kname(self): |
387 | - """returns a dasd kname if device_id is valid and online """ |
388 | - result = self.dasd.kname |
389 | - self.assertIsNotNone(result) |
390 | - self.assertEqual(result, self.m_listdir.return_value[0]) |
391 | - |
392 | - def test_devid_to_kname_raises_runtimeerror_no_online(self): |
393 | - """device_id_to_kname raises RuntimeError on offline devices.""" |
394 | - self.m_online.return_value = False |
395 | - result = None |
396 | - with self.assertRaises(RuntimeError): |
397 | - result = self.dasd.kname |
398 | - self.assertEqual(result, None) |
399 | - self.assertEqual(1, self.m_online.call_count) |
400 | - self.assertEqual(0, self.m_isdir.call_count) |
401 | - self.assertEqual(0, self.m_listdir.call_count) |
402 | - |
403 | - def test_devid_to_kname_raises_runtimeerror_no_blockpath(self): |
404 | - """device_id_to_kname raises RuntimeError on invalid sysfs path.""" |
405 | - self.m_isdir.return_value = False |
406 | - result = None |
407 | - with self.assertRaises(RuntimeError): |
408 | - result = self.dasd.kname |
409 | - self.assertEqual(result, None) |
410 | - self.assertEqual(1, self.m_online.call_count) |
411 | - self.assertEqual(1, self.m_isdir.call_count) |
412 | - self.m_isdir.assert_called_with( |
413 | - '/sys/bus/ccw/devices/%s/block' % self.dasd.device_id) |
414 | - self.assertEqual(0, self.m_listdir.call_count) |
415 | - |
416 | - def test_devid_to_kname_raises_runtimeerror_empty_blockdir(self): |
417 | - """device_id_to_kname raises RuntimeError on empty blockdir.""" |
418 | - self.m_listdir.return_value = [] |
419 | - result = None |
420 | - with self.assertRaises(RuntimeError): |
421 | - result = self.dasd.kname |
422 | - self.assertEqual(result, None) |
423 | - self.assertEqual(1, self.m_online.call_count) |
424 | - self.assertEqual(1, self.m_isdir.call_count) |
425 | - self.m_isdir.assert_called_with( |
426 | - '/sys/bus/ccw/devices/%s/block' % self.dasd.device_id) |
427 | - self.m_listdir.assert_called_with( |
428 | - '/sys/bus/ccw/devices/%s/block' % self.dasd.device_id) |
429 | - self.assertEqual(1, self.m_listdir.call_count) |
430 | - |
431 | - |
432 | class TestDasdCcwDeviceAttr(CiTestCase): |
433 | |
434 | def setUp(self): |
435 | super(TestDasdCcwDeviceAttr, self).setUp() |
436 | self.add_patch('curtin.block.dasd.os.path.isfile', 'm_isfile') |
437 | self.add_patch('curtin.block.dasd.util.load_file', 'm_loadfile') |
438 | - dpath = 'curtin.block.dasd.DasdDevice' |
439 | - self.add_patch(dpath + '.kname', 'm_kname') |
440 | + self.add_patch('curtin.block.dasd.glob.glob', 'm_glob') |
441 | |
442 | # defaults |
443 | self.m_isfile.return_value = True |
444 | self.m_loadfile.return_value = self.random_string() |
445 | self.dasd = dasd.DasdDevice(random_device_id()) |
446 | - self.kname = self.random_string() |
447 | - self.m_kname.return_value = self.kname |
448 | |
449 | def _test_ccw_attr(self, my_attr=None, attr_val_in=None, attr_val=None): |
450 | if not my_attr: |
451 | @@ -200,60 +138,37 @@ class TestDasdCcwDeviceAttr(CiTestCase): |
452 | self.m_loadfile.return_value = self.random_string() |
453 | self.assertFalse(self.dasd.is_not_formatted()) |
454 | |
455 | - def test_is_online_returns_false_if_not_online(self): |
456 | - self.m_loadfile.return_value = self.random_string() |
457 | - self.assertFalse(self.dasd.is_online()) |
458 | - |
459 | def test_blocksize(self): |
460 | blocksize_val = '%d' % random.choice([512, 1024, 2048, 4096]) |
461 | + path = self.random_string() |
462 | + self.m_glob.return_value = [path] |
463 | self.m_loadfile.return_value = blocksize_val |
464 | self.assertEqual(blocksize_val, self.dasd.blocksize()) |
465 | + self.m_loadfile.assert_called_once_with(path) |
466 | |
467 | |
468 | class TestDiskLayout(CiTestCase): |
469 | |
470 | - layouts = { |
471 | - 'not-formatted': dasd.Dasdvalue(0x0, 0, 'not-formatted'), |
472 | - 'ldl': dasd.Dasdvalue(0x1, 1, 'ldl'), |
473 | - 'cdl': dasd.Dasdvalue(0x2, 2, 'cdl'), |
474 | - } |
475 | - |
476 | def setUp(self): |
477 | super(TestDiskLayout, self).setUp() |
478 | - self.add_patch('curtin.block.dasd.os.path.exists', 'm_exists') |
479 | - self.add_patch('curtin.block.dasd.dasdview', 'm_dasdview') |
480 | + self.add_patch('curtin.block.dasd.dasd_format', 'm_dasd_format') |
481 | dpath = 'curtin.block.dasd.DasdDevice' |
482 | self.add_patch(dpath + '.devname', 'm_devname') |
483 | |
484 | - # defaults |
485 | - self.m_exists.return_value = True |
486 | - self.m_dasdview.return_value = self._mkview() |
487 | self.dasd = dasd.DasdDevice(random_device_id()) |
488 | self.devname = self.random_string() |
489 | self.m_devname.return_value = self.devname |
490 | |
491 | - @classmethod |
492 | - def _mkview(cls, layout=None): |
493 | - if not layout: |
494 | - layout = random.choice(list(cls.layouts.values())) |
495 | - return {'extended': {'format': layout}} |
496 | - |
497 | - # XXX: Parameterize me |
498 | - def test_disk_layout_returns_dasd_extended_format_value(self): |
499 | + def test_disk_layout_returns_dasd_format_result(self): |
500 | """disk_layout returns dasd disk_layout format as string""" |
501 | - for my_layout in self.layouts.values(): |
502 | - self.m_dasdview.return_value = self._mkview(layout=my_layout) |
503 | - self.assertEqual(my_layout.txt, self.dasd.disk_layout()) |
504 | + expected = self.m_dasd_format.return_value = self.random_string() |
505 | + self.assertEqual(expected, self.dasd.disk_layout()) |
506 | |
507 | - # XXX: Parameterize me |
508 | - def test_disk_layout_raises_valueerror_on_missing_format_section(self): |
509 | + def test_disk_layout_raises_valueerror_on_missing_format(self): |
510 | """disk_layout raises ValueError if view missing 'format' section.""" |
511 | - for my_layout in self.layouts.values(): |
512 | - view = self._mkview(layout=my_layout) |
513 | - view['extended']['format'] = None |
514 | - self.m_dasdview.return_value = view |
515 | - with self.assertRaises(ValueError): |
516 | - self.dasd.disk_layout() |
517 | + self.m_dasd_format.return_value = None |
518 | + with self.assertRaises(ValueError): |
519 | + self.dasd.disk_layout() |
520 | |
521 | |
522 | class TestLabel(CiTestCase): |
523 | @@ -287,8 +202,6 @@ class TestLabel(CiTestCase): |
524 | |
525 | class TestNeedsFormatting(CiTestCase): |
526 | |
527 | - blocksizes = [512, 1024, 2048, 4096] |
528 | - |
529 | def setUp(self): |
530 | super(TestNeedsFormatting, self).setUp() |
531 | dpath = 'curtin.block.dasd.DasdDevice' |
532 | @@ -298,8 +211,10 @@ class TestNeedsFormatting(CiTestCase): |
533 | self.add_patch(dpath + '.label', 'm_label') |
534 | |
535 | self.m_not_fmt.return_value = False |
536 | - self.m_blocksize.return_value = 4096 |
537 | - self.m_disk_layout.return_value = TestDiskLayout.layouts['cdl'].txt |
538 | + self.blocksize = 4096 |
539 | + self.m_blocksize.return_value = self.blocksize |
540 | + self.disk_layout = self.random_string() |
541 | + self.m_disk_layout.return_value = self.disk_layout |
542 | self.label = self.random_string() |
543 | self.m_label.return_value = self.label |
544 | |
545 | @@ -308,26 +223,34 @@ class TestNeedsFormatting(CiTestCase): |
546 | def test_needs_formatting_label_mismatch(self): |
547 | my_label = self.random_string() |
548 | self.assertNotEqual(self.label, my_label) |
549 | - self.assertTrue(self.dasd.needs_formatting(4096, 'cdl', my_label)) |
550 | + self.assertTrue( |
551 | + self.dasd.needs_formatting( |
552 | + self.blocksize, self.disk_layout, my_label)) |
553 | |
554 | def test_needs_formatting_layout_mismatch(self): |
555 | - my_layout = TestDiskLayout.layouts['ldl'].txt |
556 | + my_layout = self.random_string() |
557 | + self.assertNotEqual(self.disk_layout, my_layout) |
558 | self.assertTrue( |
559 | - self.dasd.needs_formatting(4096, my_layout, self.label)) |
560 | + self.dasd.needs_formatting( |
561 | + self.blocksize, my_layout, self.label)) |
562 | |
563 | def test_needs_formatting_blocksize_mismatch(self): |
564 | - my_blocksize = random.choice(self.blocksizes[0:3]) |
565 | + my_blocksize = random.randrange(self.blocksize) |
566 | + self.assertNotEqual(self.blocksize, my_blocksize) |
567 | self.assertTrue( |
568 | - self.dasd.needs_formatting(my_blocksize, 'cdl', self.label)) |
569 | + self.dasd.needs_formatting( |
570 | + my_blocksize, self.disk_layout, self.label)) |
571 | |
572 | def test_needs_formatting_unformatted_disk(self): |
573 | self.m_not_fmt.return_value = True |
574 | self.assertTrue( |
575 | - self.dasd.needs_formatting(4096, 'cdl', self.label)) |
576 | + self.dasd.needs_formatting( |
577 | + self.blocksize, self.disk_layout, self.label)) |
578 | |
579 | def test_needs_formatting_ignores_label_mismatch(self): |
580 | self.assertFalse( |
581 | - self.dasd.needs_formatting(4096, 'cdl', None)) |
582 | + self.dasd.needs_formatting( |
583 | + self.blocksize, self.disk_layout, None)) |
584 | |
585 | |
586 | class TestFormat(CiTestCase): |
587 | @@ -346,10 +269,6 @@ class TestFormat(CiTestCase): |
588 | self.devname = self.random_string() |
589 | self.m_devname.return_value = self.devname |
590 | |
591 | - def test_format_devname_ignores_path_missing_strict_false(self): |
592 | - self.m_exists.return_value = False |
593 | - self.dasd.format(strict=False) |
594 | - |
595 | def test_format_defaults_match_docstring(self): |
596 | self.dasd.format() |
597 | self.m_subp.assert_called_with( |
598 | @@ -454,9 +373,39 @@ class TestDasdInfo(CiTestCase): |
599 | dasd.dasdinfo(device_id) |
600 | |
601 | |
602 | -class TestDasdView(CiTestCase): |
603 | +class TestDasdFormat(CiTestCase): |
604 | + |
605 | + def setUp(self): |
606 | + super(TestDasdFormat, self).setUp() |
607 | + self.add_patch('curtin.block.dasd.util.subp', 'm_subp') |
608 | + self.add_patch('curtin.block.dasd.os.path.exists', 'm_exists') |
609 | + self.add_patch('curtin.block.dasd._dasd_format', 'm_dasd_format') |
610 | + |
611 | + # defaults |
612 | + self.m_exists.return_value = True |
613 | + self.m_subp.return_value = ('', '') |
614 | + |
615 | + def test_dasd_format_raise_error_on_invalid_device(self): |
616 | + """dasdview raises error on invalid device path.""" |
617 | + devname = self.random_string() |
618 | + self.m_exists.return_value = False |
619 | + with self.assertRaises(ValueError): |
620 | + dasd.dasd_format(devname) |
621 | + |
622 | + def test_dasd_foramt_calls__dasd_format(self): |
623 | + """dasdview calls parser on output from dasdview command.""" |
624 | + devname = self.random_string() |
625 | + view = self.random_string() |
626 | + self.m_subp.return_value = (view, self.random_string()) |
627 | + dasd.dasd_format(devname) |
628 | + self.m_subp.assert_called_with( |
629 | + ['dasdview', '--extended', devname], capture=True) |
630 | + self.m_dasd_format.assert_called_with(view) |
631 | + |
632 | + |
633 | +class Test_DasdFormat(CiTestCase): |
634 | |
635 | - view = textwrap.dedent("""\ |
636 | + view_output_template = textwrap.dedent("""\ |
637 | |
638 | --- general DASD information ---------------------------------------------- |
639 | device node : /dev/dasdd |
640 | @@ -485,7 +434,7 @@ class TestDasdView(CiTestCase): |
641 | FBA_layout : hex 0 dec 0 |
642 | characteristics_size : hex 40 dec 64 |
643 | confdata_size : hex 100 dec 256 |
644 | - format : hex 2 dec 2 CDL formatted |
645 | + format : hex 2 dec 2 {format} |
646 | features : hex 0 dec 0 default |
647 | |
648 | characteristics : 3990e933 900c5e0c 39f72032 2721000f |
649 | @@ -510,59 +459,15 @@ class TestDasdView(CiTestCase): |
650 | 81000003 2d001e00 15000247 000c0016 |
651 | 000cc018 935e41ee 00030000 0000a000""") |
652 | |
653 | - view_nondasd = textwrap.dedent("""\ |
654 | - Error: dasdview: Could not retrieve disk information! |
655 | - |
656 | - """) |
657 | - |
658 | - def setUp(self): |
659 | - super(TestDasdView, self).setUp() |
660 | - self.add_patch('curtin.block.dasd.util.subp', 'm_subp') |
661 | - self.add_patch('curtin.block.dasd.os.path.exists', 'm_exists') |
662 | - self.add_patch('curtin.block.dasd._parse_dasdview', 'm_parseview') |
663 | - |
664 | - # defaults |
665 | - self.m_exists.return_value = True |
666 | - self.m_subp.return_value = ('', '') |
667 | - |
668 | - def test_dasdview_raise_error_on_invalid_device(self): |
669 | - """dasdview raises error on invalid device path.""" |
670 | - devname = self.random_string() |
671 | - self.m_exists.return_value = False |
672 | - with self.assertRaises(ValueError): |
673 | - dasd.dasdview(devname) |
674 | - |
675 | - def test_dasdview_calls_parse_dasdview(self): |
676 | - """dasdview calls parser on output from dasdview command.""" |
677 | - devname = self.random_string() |
678 | - self.m_subp.return_value = (self.view, self.random_string()) |
679 | - dasd.dasdview(devname) |
680 | - self.m_subp.assert_called_with( |
681 | - ['dasdview', '--extended', devname], capture=True) |
682 | - self.m_parseview.assert_called_with(self.view) |
683 | - |
684 | - |
685 | -class TestParseDasdView(CiTestCase): |
686 | - |
687 | - def test_parse_dasdview_no_input(self): |
688 | - """_parse_dasdview raises ValueError on invalid status input.""" |
689 | - for view_output in [None, 123, {}, (), []]: |
690 | - with self.assertRaises(ValueError): |
691 | - dasd._parse_dasdview(view_output) |
692 | - |
693 | - def test_parse_dasdview_invalid_strings_short(self): |
694 | - """_parse_dasdview raises ValueError on invalid long input.""" |
695 | - with self.assertRaises(ValueError): |
696 | - dasd._parse_dasdview("\n".join([self.random_string()] * 20)) |
697 | - |
698 | - def test_parse_dasdview_returns_dictionary(self): |
699 | + def test__dasd_format_returns_format(self): |
700 | """_parse_dasdview returns dict w/ required keys parsing valid view.""" |
701 | - result = dasd._parse_dasdview(TestDasdView.view) |
702 | - self.assertEqual(dict, type(result)) |
703 | - self.assertNotEqual({}, result) |
704 | - self.assertEqual(3, len(result.keys())) |
705 | - for key in result.keys(): |
706 | - self.assertEqual(dict, type(result[key])) |
707 | - self.assertNotEqual(0, len(list(result[key].keys()))) |
708 | + for (format, expected) in [ |
709 | + ('CDL formatted', 'cdl'), |
710 | + ('LDL formatted', 'ldl'), |
711 | + ('not formatted', 'not-formatted'), |
712 | + ]: |
713 | + result = dasd._dasd_format( |
714 | + self.view_output_template.format(format=format)) |
715 | + self.assertEqual(result, expected) |
716 | |
717 | # vi: ts=4 expandtab syntax=python |
FAILED: Continuous integration, rev:b070e5b7032 f2a78c342d21811 9cc976f233b247 /jenkins. ubuntu. com/server/ job/curtin- ci/14/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-amd64/ 14/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-arm64/ 14/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-ppc64el/ 14/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-s390x/ 14/
https:/
Executed test runs:
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/curtin- ci/14// rebuild
https:/